定义
将一个类的接口,转换成客户期望的另一个类的接口。适配器让原本接口不兼容的类可以合作无间。说通俗点其实就是一个东西不适用的时候给它包裹一层让他能够适配使用又不改变他本身。
分类
(1)类适配器:
当客户在接口中定义了他期望的行为时,我们就可以应用适配器模式,提供一个实现该接口的类,并且扩展已有的类,通过创建子类来实现适配。
public interface Target { /** * 这是源类Adaptee也有的方法 */ public void sampleOperation1(); /** * 这是源类Adapteee没有的方法 */ public void sampleOperation2(); }
public class Adaptee { public void sampleOperation1(){} }
public class Adapter extends Adaptee implements Target { /** * 由于源类Adaptee没有方法sampleOperation2() * 因此适配器补充上这个方法 */ @Override public void sampleOperation2() { //写相关的代码 } }
(2)对象适配器:
对象适配器”通过组合除了满足“用户期待接口”还降低了代码间的不良耦合。在工作中推荐使用“对象适配”。
public class Adapter { private Adaptee adaptee; public Adapter(Adaptee adaptee){ this.adaptee = adaptee; } /** * 源类Adaptee有方法sampleOperation1 * 因此适配器类直接委派即可 */ public void sampleOperation1(){ this.adaptee.sampleOperation1(); } /** * 源类Adaptee没有方法sampleOperation2 * 因此由适配器类需要补充此方法 */ public void sampleOperation2(){ //写相关的代码 } }
(3) 缺省适配器模式:
缺省适配器模式是一种特殊的适配器模式,但这个适配器是由一个抽象类实现的,并且在抽象类中要实现目标接口中所规定的所有方法,但很多方法的实现都是“平庸”的实现,也就是说,这些方法都是空方法。而具体的子类都要继承此抽象类。
public interface Job { public abstract void speakJapanese(); public abstract void speakEnglish(); public abstract void speakFrench(); public abstract void speakChinese(); }
public abstract class JobDefault implements Job{ public void speakChinese() { } public void speakEnglish() { } public void speakFrench() { } public void speakJapanese() { } }
public class JobImpl extends JobDefault{ public void speakChinese(){ System.out.println("I can speak Chinese!"); } }
实例
java的迭代类Iterator就使用了适配器模式,早期的集合类型就是被适配对象,iterator是接口,每一个集合类型都有适配类。
优点
适配器模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
1.更好的复用性:解决了现存类和复用环境要求不一致的问题。系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
2.更好的扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
3.将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
4.一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
缺点
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
使用场景
1. 系统需要使用现有的类,而此类的接口不符合系统的需要。
2. 讲彼此没有太大关联的类包括一些可能在将来引进的类引进来一起完成某项工作(指对象适配)。
3. (对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
对象适配和类适配比较
类的适配模式用于单一源的适配,由于它的源的单一化,代码实现不用写选择逻辑,很清晰;
而对象的适配模式则可用于多源的适配,弥补了类适配模式的不足,使得原本用类适配模式需要写很多适配器的情况不复存在,弱点是,由于源的数目可以较多,所以具体的实现条件选择分支比较多,不太清晰。
对象适配器采用的是组合的形式,不仅可以适配某个对象,还能适配该类的任何子类(对象适配因为是继承,是静态关系继承了被适配对象以后就不能处理子类),更富弹性,符合松耦合的编程规则;
而类适配器采用的是继承的形式,不需要重新实现整个被适配对象,必要的时候可以覆盖被适配者的行为,弹性不够但是效率更高。对于对象适配器,要重定义被适配对象的行为比较困难,这种情况下,需要定义被适配对象子类来实现重定义,然后让适配器组合子类。虽然重定义被适配对象的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。