概念:
适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
本章代码: 小麻雀icknn/设计模式练习 - Gitee.com
结构:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
UML类图:
例:
目标(Target)接口:
package com.study.main.Adapter;public interface Usb {String useUsbMethod();
}
适配器(Adapter)类:
package com.study.main.Adapter;public class ChangeAdapter implements Usb{AndroidTypeC androidTypeC;public ChangeAdapter() {this.androidTypeC = new AndroidTypeC();}@Overridepublic String useUsbMethod() {return androidTypeC.myUsbMethods();}
}
适配者(Adaptee)类:
package com.study.main.Adapter;public class AndroidTypeC {String myUsbMethods(){return "Android TypeC 接口";}
}
package com.study.main.Adapter;public class Android implements Usb{@Overridepublic String useUsbMethod() {return "Android USB 适配 Mini-USB";}
}
运行&结果:
场景分析 :
适配器模式一般常用的使用场景有:
- 原有接口无法修改时;
- 原有接口功能太老旧时;
- 统一多个类的接口设计时;
- 需要过渡升级旧接口时;
- 需要依赖外部系统时;
- 适配不同数据格式时;
- 不同接口协议转换时。
适配器模式的使用场景主要有这两大类:第一类就是原有接口功能不满足现有要求,需要在兼容老接口的同时做适当的扩展;第二类是有相似性的多个不同接口之间做功能的统一
适配器在源码中的应用 :
在inputStreamReader中适配了inpuStream类并使用了里面的方法把InputStream对象转换成了BufferReader
适配器模式优缺点:
优点:
- 将目标类和具体适配者类解耦。 通过引入一个适配器类来兼容现有的目标类,重用原有类功能的同时扩展新功能,而无须修改原有目标类代码,这样很好地避免了具体适配者类和目标类的直接耦合。
- 增加了类的透明性。 具体的适配者类中新增功能只影响适配者类,而对于使用目标类的客户端类来说是透明的(使用目标类接口),客户端的调用逻辑不会受到影响。
- 满足里氏替换原则 具体适配者类通过适配器类与目标类进行交互,那么适配器类只要不影响目标类的接口功能,具体适配者类无论使用什么样的新功能,都能很方便快速地进行替换。
- 符合开闭原则 由于具体适配者类要么是适配器类的子类,要么和适配器类是组合关系,所以对目标类没有修改,满足开闭原则。
- 统一多个类或接口 一个适配器类可以把多个不同的具体适配者类和子类,都适配到同一个目标类上,如果这个目标类是一个新类,那么就是间接实现了统一多个类或接口的功能。
缺点:
- 一次只能适配一个抽象类或接口
像 Java、C# 等编程语言是不支持多重继承的,那么在进行适配时,一次最多只能适配一个适配者类。另外,目标类只能为抽象类或接口,不能为具体实例类,这样会在适配时增加很多类文件和代码量,如果适配的类或接口比较多,那么就会增加代码的理解难度。 - 过度嵌套会导致接口臃肿 适配器有一个最大的弊端就是,一旦不停地在同一个目标类上增加适配器,就会很容易让接口变得越来越臃肿。你见过一个接口被适配 20 次的情景吗?我前不久在工作中就见过,其实这也是开闭原则极端副作用的某种体现。因为不想去修改原有接口,所以就不断使用新接口适配,而维护接口的人又在不断变化,于是就继续按照这个不修改的思路维护下去,表面上的确符合开闭原则,但实际上只不过是将风险不断隐藏罢了。一旦原始接口(目标类)功能下线后,这个适配链条造成的影响会非常大。
- 目标接口依赖太多适配接口,修改目标接口会导致所有适配接口都需要定制修改。 本来适配器模式是为了解耦,但是如果适配太多接口,就会演变为另一种定制化的开发。比如,上游平台商家提供的接口变更,导致下游使用方频繁变更接口。再比如,消息组件接口的变更导致所有引用消息组件的适配器全部都需要修改。