这是我参与更文挑战的第16天,活动详情查看: 更文挑战
前言
今日,我们来学习结构型模式的第二种—-适配器模式
其他设计模式介绍:
一 .适配器模式入门
1.1 什么是适配器?
适配器这个词也很好理解,我们天天都在使用,例如下面的显示器接口转换器,以及type-c和耳机的转换器.这很好理解吧,当两个东西互相不能匹配的时候,都不愿意为对方改变自己的时候,咱们给它加个适配器,这样他们可以正常工作在一起.
百度百科定义:
在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
适配器模式(Adapter Pattern)中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系可以划分为对象适配器和类适配器两种。
- 对象适配器:通过组合方式实现
- 类适配器:通过继承或实现实现
1.2 适配器结构
适配器模式种包含三个角色:
- 1、目标接口:当前业务所期待的接口,可以是抽象类或接口
- 2、适配者类: 它是被访问和适配的现存组件库中的组件接口
- 3、适配器类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让访问者按目标接口访问适配者
在举个例子来说明下这三个角色分别对应着什么。
这里就拿我特别喜欢的一本书来举例,我们知道《三体》是超越时代的一本书,大刘因为这本书也成为首个获得雨果奖的亚洲作者,而这本书在全球也有很大的粉丝群体,其中最大咖当属奥巴马,他甚至以国家名义向作者催稿。好,打住!我们就拿这个案例来说明,奥巴马想看三体,但他不懂中文,怎么办?
三种办法:
- 1、让大刘写个英文的三体?显然不行,会出现很多语法错误
- 2、让奥巴马学中文,也不行。
- 3、找个翻译,翻译成英文
这个翻译就是适配器。真正的三体译者是小刘。
那么适配者类就是三体,目标接口就是看书,适配器类就是小刘。
二、实现方式
2.1 类适配器
实现方式就是:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
也就是这个适配器小刘 它把继承了适配者类的组件,又实现了目标接口的子类。在这个例子中就是小刘他 既懂中文又懂英语。
画个UML方便理解:
// 1、定义一个英语目标接口
public interface EngLishBook {
String readEBook();
}
// 2、实现这个英语目标接口
public class EngLishBookImpl implements EngLishBook {
@Override
public String readEBook() {
String msg="i want read threebody !!!!!";
return msg;
}
}
// 3、定义一个中文适配者接口
public interface ChineseBook {
String readCBook();
}
// 4、实现这个中文适配者接口
public class ChineseBookImpl implements ChineseBook{
@Override
public String readCBook() {
String msg="我 奥巴马,想看三体!!!!";
return msg;
}
}
// 5、定义一个奥巴马,它只能看英语接口
public class AoBaMa {
public String readBook(EngLishBook engLishBook){
return engLishBook.readEBook();
}
}
// 6、定义一个适配器,完成他梦想
public class XiaoLiu extends ChineseBookImpl implements EngLishBook{
@Override
public String readEBook() {
System.out.println("它想看中文三体,我得帮他翻译过来");
return readCBook();
}
}
复制代码
测试:
public class Test {
public static void main(String[] args) {
// 1. 定义一个奥巴马
AoBaMa aoBaMa = new AoBaMa();
// 2. 定义一本英语书
EngLishBookImpl engLishBook = new EngLishBookImpl();
// 3. 奥巴马只能看英语书
System.out.println(aoBaMa.readBook(engLishBook));
System.out.println("=====");
// 4. 定义一个适配器
XiaoLiu adapter = new XiaoLiu();
// 4. 想看中文书去找小刘
System.out.println(aoBaMa.readBook(adapter));
}
}
复制代码
输出结果:
i want read threebody !!!!!
=====
它想看中文三体,我得帮他翻译过来
我 奥巴马,想看三体!!!!
复制代码
类适配器的典型应用如上,它在只有一个接口规范的情况 下可用,反之就不可用。同时用继承来实现,使用场景非常受限,因为java是单继承的。
2.2 对象适配器模式
实现方式:对象适配器模式采用将现有的组件引入适配器类中,该类同时实现当前系统的业务接口。
将uml图进行改写,如下:
我们只需修改适配器类和测试类
// 创建适配器对象
public class XiaoLiu2 implements EngLishBook{
private ChineseBook chineseBook;
public XiaoLiu2(ChineseBook chineseBook){
this.chineseBook=chineseBook;
}
@Override
public String readEBook() {
System.out.println("帮他 翻译成中文");
return chineseBook.readCBook();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
// 1. 定义一个奥巴马
AoBaMa aoBaMa = new AoBaMa();
// 2. 定义一本英语书
EngLishBookImpl engLishBook = new EngLishBookImpl();
// 3. 奥巴马只能看英语书
System.out.println(aoBaMa.readBook(engLishBook));
System.out.println("=====");
// 4 定义一本中文书
ChineseBook chineseBook = new ChineseBookImpl();
// 5. 定义一个适配器
XiaoLiu2 adapter = new XiaoLiu2(chineseBook);
// 6. 想看中文书去找小刘
System.out.println(aoBaMa.readBook(adapter));
}
}
复制代码
也就是不采用继承方式,采用传参调用构造函数。更加灵活些。
三、总结
主要优点:
- 1、目标类和适配者类解耦,通过适配器重用现有的适配者类,无需修改原有结构
- 2、增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端是透明的
- 3、可以在不修改原有代码的基础上增加新的适配器类,完全符合开闭原则。
应用场景:
- 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
- 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
源码应用:
- JDK源码的IO模块用到,例如 java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream)。
- mybatis源码日志模块用到对象适配器模式。
以上,就是关于适配器模式的分享。