结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式
它分为类结构型模式
和对象结构型模式
,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性
1. 背景
在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用计算机访问照相机的 SD 内存卡时需要一个读卡器等
在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题
2. 定义
适配器模式(Adapter)的定义如下: 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
具体的使用场景如下:
-
已经存在的类的接口不符合我们的需求;
-
创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;
-
在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类
目标(Target)接口: 当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类: 它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类: 它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
优点
- 客户端通过适配器可以透明地调用目标接口。
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
缺点
- 对类适配器来说,更换适配器的实现过程比较复杂
3. 适配器模式
适配器模式分为类结构型模式
和对象结构型模式
两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些(网上有的说还有接口模式,好像不重要)
3.1 类适配器
Adapter
类通过继承Adaptee
类,实现Target
接口,完成Adaptee
到Target
的转换
以充电器的例子来说,充电器本身是Adapter
,220v的交流电是Adaptee
,我们的目标则是被转换后的5v的直流电
package adapter;
//目标接口
interface Target{
public void request();
}
//被适配者
class Adaptee{
public void specificRequest(){
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target{
public void request(){
specificRequest();
}
}
//客户端代码
public class ClassAdapterTest{
public static void main(String[] args){
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
缺点:
① Java是单继承机制,所以类适配器要求继承adaptee
类这一点算是一个缺点,因为要求target
必须是一个接口,有一定的局限性;
比如说我们现在有一个需求:我们有一个能从数据库中抽取文本的工具类TextTool
,一个抽取图片的工具类ImageTool
;现在我们又需要一个抽取合成富文本的工具类(既有文字又有图片),而抽取文字和图片的功能前面都实现了,那我们就可以使用适配器模式去继承TextTool
和ImageTool
,但是由于Java的单继承机制,这显然是办不到的
② adaptee
类的方法在Adapter
中完全暴露了出来,也增加了使用成本
优点:
由于其继承adaptee
,所以可以根据需求重写adaptee
类的方法,使adapter
的灵活性增强
3.2 对象适配器
上面的问题主要就出在继承上,而根据合成复用规则
,在系统中尽量使用关联关系来替代继承关系,于是就有了对象适配器
对象适配器的基本思路和类的适配器模式相同,只是将Adapter
类做修改,不是继承Adaptee
类,而是持有Adaptee
的实例,以解决兼容性问题,即持有Adaptee
,实现target
接口,完成adaptee
到target
的转换
package adapter;
//目标接口
interface Target{
public void request();
}
//被适配者
class Adaptee{
public void specificRequest(){
System.out.println("适配者中的业务代码被调用!");
}
}
//对象适配器类
class ObjectAdapter implements Target{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee){
this.adaptee=adaptee;
}
public void request(){
adaptee.specificRequest();
}
}
//客户端代码
public class ObjectAdapterTest{
public static void main(String[] args){
System.out.println("对象适配器模式测试:");
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
实际我认为不就是对原有代码的一种巧妙复用么…