介绍
适配器模式实际上就是一个转换的过程,把不能一起工作的两样东西通过转换,让他们能够在一起工作。在现实中的例子比如:手机的充电器,充电器的接头,有的是把两相电转换为三相电的,当然也有把三相电转换成两相电的。使用笔记本电脑,笔记本电脑的工作电压和我们家里照明电压是不一致的,当然也就需要充电器把照明电压转换成笔记本的工作电压,只有这样笔记本电脑才可以正常工作。
动机
在软件系统中,由于应用环境的变化,常常需要将一些现存的对象放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?
意图
将一个类的接口转换成客户希望的另一个接口。Adapter模式
使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
结构图
适配器有两种类型:一种是对象适配器,一种是类适配器,相对而言,对象适配器更为常用。
对象适配器
对象适配器使用的是对象组合的方案,它的Adapter
和Adaptee
的关系是组合关系。OO
中优先使用组合模式,组合模式不适用再考虑继承。因为组合模式更加松耦合,而继承是紧耦合的,父类的任何改动都要导致子类的改动。
类适配器
类适配器使用的是继承的方案,Adapter
和Adaptee
的关系是继承关系。
角色组成
- 目标角色(Target):定义
Client
使用的与特定领域相关的接口。 - 客户角色(Client):与符合
Target
接口的对象协同。 - 被适配角色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。
- 适配器角色(Adapte):适配器模式的核心。它将对被适配
Adaptee
角色已有的接口转换为目标角色Target
匹配的接口。对Adaptee
的接口与Target
接口进行适配.
实现
对象适配器的实现
1 | namespace 对象的适配器模式 |
类适配器的实现
1 | namespace 设计模式之适配器模式 |
实现要点
Adapter模式
主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。GoF23定义了两种Adapter模式的实现结构:
对象适配器
和类适配器
类适配器
采用多继承的实现方式,在C#语言中,如果被适配角色是类,Target的实现只能是接口,因为C#语言只支持接口的多继承的特性。在C#语言中类适配器也很难支持适配多个对象的情况,同时也会带来了不良的高耦合和违反类的职责单一的原则,所以一般不推荐使用。对象适配器
采用对象组合的方式,更符合松耦合精神,对适配的对象也没限制,可以一个,也可以多个,但是,使得重定义Adaptee
的行为较困难,这就需要生成Adaptee
的子类并且使得Adapter
引用这个子类而不是引用Adaptee
本身。
Adapter模式
可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter
模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。Adapter模式
本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
优缺点
对象适配器的优缺点
优点
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
- 采用 “对象组合”的方式,更符合松耦合。
缺点
- 使得重定义
Adaptee
的行为较困难,这就需要生成Adaptee
的子类并且使得Adapter
引用这个子类而不是引用Adaptee
本身。
类适配器的优缺点
优点
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
- 可以重新定义
Adaptee
(被适配的类)的部分行为,因为在类适配器模式中,Adapter
是Adaptee
的子类 - 仅仅引入一个对象,并不需要额外的字段来引用
Adaptee
实例(这个即是优点也是缺点)。缺点
- 用一个具体的
Adapter
类对Adaptee
和Target
进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee
的实例,光调用this.SpecificRequest
方法并不能去调用它对应子类的SpecificRequest
方法。 - 采用了 “多继承”的实现方式,带来了不良的高耦合。
适配器的使用场景
- 系统需要复用现有类,而该类的接口不符合系统的需求
- 想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
- 对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。