适配器模式--Adapter Pattern
模拟场景:很多人都喜欢看NBA吧,姚明进驻NBA,打开了中国的市场。虽然后面姚明在NBA打得还不错,但是在刚进入NBA篮坛的时候,并不是那么顺利的。语言交流就是一个最大的问题。刚开始打球期间,教练及队员的战术部署姚明都无法理解,所以他需要这么一个翻译者,将教练及队员的意思转达给姚明,这样才可以进行合作。
现在进行场景的模拟,先不考虑那么多。假如姚明一开始进入NBA的时候就已经会英语,可以进行交流了。那么这个时候教练就可以进行战术的部署了。
转换成类,所有的队员都要服从教练的战术要求,假设现在教练都要给队员部署进攻和防守的指令。所有大家接收的动作是一致的,只是实现不一样而已。所有这里需要先定义出一个接口或者抽象类。
IPlayer
1 package com.zqz.dp.adapter; 2 /** 3 * @author Qin 所有运动员的父接口,完成同样的动作 4 */ 5 public interface IPlayer { 6 /** 7 * 运动员进攻 8 */ 9 void attack(); 10 /** 11 * 运动员防守 12 */ 13 void defense(); 14 }
构建完接口之后,就是对接口进行实现了。假设现在有前锋forwards、中锋center、后卫guards三种运动员,来根据教练的指令完成不同的动作。
Forwards
1 package com.zqz.dp.adapter; 2 /** 3 * @author Qin 4 * 前锋类,实现运动员接口,实现自己的动作 5 */ 6 public class Forwards implements IPlayer { 7 private String name; //定义运动员的名字 8 public Forwards(String name) { //构造方法赋值 9 this.name = name; 10 } 11 @Override 12 public void attack() { 13 System.out.println("前锋〖"+this.name+"〗攻击。。。"); 14 } 15 @Override 16 public void defense() { 17 System.out.println("前锋〖"+this.name+"〗防守。。。"); 18 } 19 }
Center
1 package com.zqz.dp.adapter; 2 /** 3 * @author Qin 4 * 中锋类,实现运动员接口,实现自己的动作 5 */ 6 public class Center implements IPlayer { 7 private String name; //定义运动员的名字 8 public Center(String name) { //构造方法赋值 9 this.name = name; 10 } 11 @Override 12 public void attack() { 13 System.out.println("中锋〖"+this.name+"〗攻击。。。"); 14 } 15 @Override 16 public void defense() { 17 System.out.println("中锋〖"+this.name+"〗防守。。。"); 18 } 19 }
Guards
1 package com.zqz.dp.adapter; 2 /** 3 * @author Qin 4 * 后卫类,实现运动员接口,实现自己的动作 5 */ 6 public class Guards implements IPlayer { 7 private String name; //定义运动员的名字 8 public Guards(String name) { //构造方法赋值 9 this.name = name; 10 } 11 @Override 12 public void attack() { 13 System.out.println("后卫〖"+this.name+"〗攻击。。。"); 14 } 15 @Override 16 public void defense() { 17 System.out.println("后卫〖"+this.name+"〗防守。。。"); 18 } 19 }
Client
1 package com.zqz.dp.adapter; 2 /** 3 * @author 4 * Qin 场景类,模拟NBA比赛 5 */ 6 public class Client { 7 public static void main(String[] args) { 8 IPlayer ba=new Forwards("巴蒂尔"); 9 ba.attack(); 10 IPlayer yao=new Center("姚明"); 11 yao.attack(); 12 yao.defense(); 13 IPlayer mai=new Guards("麦迪"); 14 mai.defense(); 15 } 16 }
以上的操作很简单,说白了就是父类引用指向子类实例,但是现在姚明是直接可以听懂了教练的战术安排了,这个已经是后面发展得很好的姚明了。并不是一开始不能进行交流的那个大姚了。
要模拟刚入NBA的大姚,那么说明需要创建一个新的类来表示大姚这个对象。在这里我们把Center改成foreignCenter,表示外籍中锋。而很明显这个时候大姚根本无法听懂教练的意思,所以需要一个翻译者translator,翻译者是可以听懂教练的意思的,但是听懂了总不能自己上场比赛吧,所以要把教练的意思传递给大姚,交给大姚去执行。
转化为类之间的关系也就是translator类要实现IPlayer接口,并且要有一个fireignCenter的引用,具体的实现交给引用去调用自己的方法。
ForeignCenter
1 package com.zqz.dp.adapter; 2 /** 3 * @author Qin 4 * 外籍中锋类,外籍中锋是新引进的类,并不能听懂教练的意思,所以只能执行自己的功能 5 */ 6 public class ForeignCenter { 7 private String name; // 定义运动员的名字 8 public ForeignCenter(String name) { // 构造方法赋值 9 this.name = name; 10 } 11 public void attack() { 12 System.out.println("外籍中锋〖" + this.name + "〗攻击。。。"); 13 } 14 public void defense() { 15 System.out.println("外籍中锋〖" + this.name + "〗防守。。。"); 16 } 17 }
Translator
1 package com.zqz.dp.adapter; 2 /** 3 * @author Qin 4 * 翻译者类,适配器对象。传递教练的意思给外籍中锋 5 */ 6 public class Translator implements IPlayer { 7 private ForeignCenter fc; 8 public Translator() { //翻译者类知道要把消息传递给谁,所以立即生成外籍中锋实例 9 fc=new ForeignCenter("大姚"); 10 } 11 @Override 12 public void attack() { 13 this.fc.attack(); //告诉大姚要进行攻击 14 } 15 @Override 16 public void defense() { 17 this.fc.defense(); //告诉大姚进行防守 18 } 19 }
其他类不变。这样就完成了功能,由翻译者转达大姚进行战术的安排。
上面的操作其实很简单,只是在执行的时候调用的是具体真实类的操作而已。是否会觉得有点跟代理类似,其实会这样觉得是因为我把ForeignCenter中的方法定义成和IPlayer的方法一致。也就是说虽然教练给大姚下达了战术的安排,但是大姚可以执意按自己的想法进行安排。所以这也是和代理不同的地方。
以上的操作就是适配器模式,准确来说是对象适配器,因为在类中传递了另一个类的引用。还有另一种叫做类适配器。要实现类适配器就方便多了,只要让Translator继承ForeignCenter即可,再调用父类的方法。很明显在这里是不合适的,因为两者不存在is-a的关系,所以在真正开发中,对象适配器用的必类适配器多。
听名字大家就应该能明白意思了,何为适配器?就是把两个不合适的对象通过一个中间对象连接起来。像我们平常的电池充电不也需要一个电源适配器吗?就是一样的道理。
适配器模式是一个补救的模式,也就是说一般在开发前期不会考虑该模式,而是在后期扩展中才会考虑到。还是以上面的例子为例,NBA事先根本就不能料想到是否会有中国球员参加比赛,所以才会有翻译者的存在,翻译者就是一个适配器。
适配模式的优点:
1、 适配器可以将两个毫无关联的对象连接起来。
2、 增加了类的透明度:也就是说教练根本不用管大姚,直接告诉翻译者如何做就行了。
3、 提高了类的复用性,易扩展,灵活性好。
适配器的使用场景:
你有动机去修改一个已经投入生产的接口时,就可以使用适配器进行扩展。
适配器的拓展:
除了类适配器和对象适配器外,还有一种缺省适配器,所谓的缺省适配器就是说在一个接口中存在了一大堆的方法,但是我们在具体的实现中根本就不需要这么多方法,那么我们就可以创建一个中间类,把这些方法全部空实现,然后具体的类再继承该空实现的类,完成自己所需要的功能。