在阎宏博士的《JAVA与模式》一书中开头是这样描述适配器(Adapter)模式的:
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
作用
类似于三相插头到两相插头转换器(适配器)所起的作用。
结构
适配器模式有类的适配器模式和对象的适配器模式这两种不同的形式。
类适配器模式
把适配类的API转换成目标类的API,见下图:
Adaptee类没有sampleOperation2方法,而客户端需要使用这个方法。Adapter类继承了Adaptee类,把Adaptee类的API与Target类的API衔接起来。
涉及到的角色:
目标(Target)角色:类适配器模式中目标不可以是类,只能是接口。
源(Adapee)角色:具体类。
适配器(Adaper)角色:具体类。
1 public interface Target { 2 /** 3 * 这是源类Adaptee也有的方法 4 */ 5 public void sampleOperation1(); 6 /** 7 * 这是源类Adapteee没有的方法 8 */ 9 public void sampleOperation2(); 10 }
1 public class Adaptee { 2 3 public void sampleOperation1(){} 4 5 }
1 public class Adapter extends Adaptee implements Target { 2 /** 3 * 由于源类Adaptee没有方法sampleOperation2() 4 * 因此适配器补充上这个方法 5 */ 6 @Override 7 public void sampleOperation2() { 8 //写相关的代码 9 } 10 11 }
对象适配器模式
对象的适配器模式把被适配的类的API转换成为目标类的API,使用委派关系连接到Adaptee类,见下图:
Adapter与Adaptee是委派关系。
1 public interface Target { 2 /** 3 * 这是源类Adaptee也有的方法 4 */ 5 public void sampleOperation1(); 6 /** 7 * 这是源类Adapteee没有的方法 8 */ 9 public void sampleOperation2(); 10 }
1 public class Adaptee { 2 3 public void sampleOperation1(){} 4 5 }
1 public class Adapter { 2 private Adaptee adaptee; 3 4 public Adapter(Adaptee adaptee){ 5 this.adaptee = adaptee; 6 } 7 /** 8 * 源类Adaptee有方法sampleOperation1 9 * 因此适配器类直接委派即可 10 */ 11 public void sampleOperation1(){ 12 this.adaptee.sampleOperation1(); 13 } 14 /** 15 * 源类Adaptee没有方法sampleOperation2 16 * 因此由适配器类需要补充此方法 17 */ 18 public void sampleOperation2(){ 19 //写相关的代码 20 } 21 }
类适配器和对象适配器比较:
类适配器使用继承方式,不能访问Adaptee子类,可以重写父类方法;而对象适配器使用对象组合方式,可以访问Adaptee子类,不能重写父类方法。
尽量使用对象适配器,少用继承。
优点:更好的复用性和扩展性。
缺点:过多的适配器会使系统变得混乱。
缺省适配模式
作为适配器模式的一个特例,缺省适配(Default Adapter)模式为一个接口提供缺省实现,子类型可以扩展这个缺省实现,不必扩展原有接口。
鲁智深的故事
和尚要做什么呢?吃斋、念经、打坐、撞钟、习武等。给出所有和尚都需要实现的方法对应的接口:
1 public interface 和尚 { 2 public void 吃斋(); 3 public void 念经(); 4 public void 打坐(); 5 public void 撞钟(); 6 public void 习武(); 7 public String getName(); 8 }
下面的鲁智深类通不过编译:
1 public class 鲁智深 implements 和尚{ 2 public void 习武(){ 3 拳打镇关西; 4 大闹五台山; 5 大闹桃花村; 6 火烧瓦官寺; 7 倒拔垂杨柳; 8 } 9 public String getName(){ 10 return "鲁智深"; 11 } 12 }
当初鲁达剃度,众僧说:“此人形容丑恶、相貌凶顽,不可剃度他",但是长老却说:”此人上应天星、心地刚直。虽然时下凶顽,命中驳杂,久后却得清净。证果非凡,汝等皆不及他。”“应”者,实现也;“天星”者,抽象类也。
1 public abstract class 天星 implements 和尚 { 2 public void 吃斋(){} 3 public void 念经(){} 4 public void 打坐(){} 5 public void 撞钟(){} 6 public void 习武(){} 7 public String getName(){ 8 return null; 9 } 10 }
抽象的天星类是一个适配器类,实现了和尚接口所要求的所有方法。
鲁智深类继承抽象类“天星”:
1 public class 鲁智深 extends 天星{ 2 public void 习武(){ 3 拳打镇关西; 4 大闹五台山; 5 大闹桃花村; 6 火烧瓦官寺; 7 倒拔垂杨柳; 8 } 9 public String getName(){ 10 return "鲁智深"; 11 } 12 }
适配器模式目的是与目标接口相容,缺省适配模式目的是提供接口的一般实现类。如果不准备实现一个接口的所有方法,可以使用缺省适配模式来创建一个抽象类,给出所有方法的一般实现,继承该抽象类的子类不必实现所有方法。
参考资料