设计模式系列 - Adapter模式

 

"将一个类的接口转换成客户希望的另一个接口,Adapter模式使原本由于接口不兼容而不能一起工作的类可以一起工作" 。简单的说,就是利用现有的接口去包装一个第三方的接口, 使其能象现有接口一样被程序调用,而不考虑实际使用类的差异

 

意图
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。(GoF)

场景
  相信很多人都知道什么是显卡,也有很多人知道显卡的本名——图形适配器。恩,是的,正好这回说说Apater模式,就拿显卡来例子来分析一下Adapter模式。
  我们知道显示器(Client)是用来显示图形的,它是不能显示数据,它只能够接受来自图形发送设备Target的信号。可是我们手头上只有CPU(Adaptee)这个产生各种描述图形的数据的数据发送器。我们需要将这些数据让显示器进行显示,可是这两个部件却是不兼容的。于是我们需要一个中间设备,它能够将CPU“适配于显示器,这便是我们的显卡——图形适配器(Adapter)

 

 

java 代码

 // 图形发送设备  

  1. public class Target {  
  2.     /** 
  3.      * 传送图形信号 
  4.      */  
  5.     public String request() {  
  6.         return "Graphic sender";  
  7.     }  

 

java 代码 

  1. // 显示器  
  2. public class Client {  
  3.   
  4.     public static void main(String[] args) {  
  5.         Target target = new Targete();  
  6.         System.out.println(target.request());  
  7.     }  
  8. }  


可是我们的CPU(Adaptee)只能输出0/1数据,他是个计算器,而不是图形发送设备(Target)

java 代码 

  1. // CPU  
  2. public class Adaptee {  
  3.     /** 
  4.      * CPU输出的数据 
  5.      */  
  6.     public String getData() {  
  7.         return "CPU data";  
  8.     }  
  9. }  


这个时候我们的显卡(Adapter)的作用便体现出来了,它负责对CPU进行适配,通过将CPU传过来的数据转换成图形信号,从而将CPU伪装成一个图形发送设备。

java 代码 

  1. // 显卡,即我们的适配器  
  2. public class Adapter extends Target {  
  3.   
  4.     // 被代理的设备  
  5.     private Adaptee apt = null;  
  6.   
  7.     /** 
  8.      * 装入被代理的设备 
  9.      */  
  10.     public Adapter(Adaptee apt) {  
  11.         this.apt = apt;  
  12.     }  
  13.   
  14.     /** 
  15.      * 被代理的设备传过来的数据转换成为图形输出 
  16.      */  
  17.     public String request() {  
  18.         return apt.getData();  
  19.     }  
  20. }  


这样,我们的电脑的显示流程就变成CPU-显卡-显示器:

java 代码 

  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.         // CPU经过显卡的适配后成了图形发送装置了  
  5.         Target target = new Adapter(new Adaptee());  
  6.         System.out.println(target.request());  
  7.     }  
  8.       
  9. }  

上面的这种依赖于对象组合的Adapter模式叫做对象适配器(Object Adapter)。它的特征是继承/实现某一方的类(Target),如这里的图形发送器,同时内部包含一个被适配的类(Adaptee),如这里的CPU。通过重写其父类的方法来进行适配。

 

 

另一种的Adapter实现
对于Adapter模式,还有另外一种实现方式,这种适配方式叫做类适配器(Class Adapter)。它与Object Adapter的不同之处在于它继承被适配的对象。

java 代码 

  1. public class Adapter extends Targer, Adaptee {  
  2.     ......  
  3. }  


这样的代码在C++中是合法的,但是在Java中规定最多只能继承一个父类,而可以实现多个接口。所以我们需要建立一个IAdaptee的接口,然后将我们的Adapter继承Target同时实现IAdaptee

java 代码 

  1. // IAdaptee接口  
  2. public interface IAdaptee {    
  3.  
  4.     String getData();  
  5. }   

 

java 代码 

  1. // Adaptee 实现IAdaptee 
  2. public class Adaptee implements IAdaptee {  
  3.   ......  
  4. }    

 

java 代码 

  1. public class Adapter extends Target implements IAdaptee {  
  2.   
  3.     private IAdaptee apt = null;  
  4.   
  5.     public Adapter(IAdaptee apt) {  
  6.         this.apt = apt;  
  7.     }  
  8.   
  9.     public String request() {  
  10.         return apt.getData();  
  11.     }  
  12.   
  13.     public String getData() {  
  14.         return apt.getData();  
  15.     }  
  16. }  


对于我们的显示器(Client)方面,Class AdapterObject Adapter一样,所以不需要进行修改。

对于Class Adapter,大家也看见了,在Adapter中因为是实现了IAdaptee接口,因此需要实现getData()的接口。一旦TargetIAdaptee拥有相同的方法时,会出现麻烦的。所以尽量优先使用Object Adapter的模式

C: 工作一年多了,纸上的笔记写了不少,但一直没有机会整理。现在离职了,就用这段时间整理一下自己的笔记,也顺便丰富一下自己的博客吧,要不也真的对不起在这里潜水两年的时间。

适配器:基于现有类所提供的服务,向客户提供接口,以满足客户的期望

==================================================================

方案1:继承

方案2:组合

方案3:给出空实现,对关心的内容注册监听器,例如awt、swing

  1. Frame frame = new Frame("new Frame"); 
  2.       frame.addMouseMotionListener(new MouseMotionAdapter() { 
  3.           @Override 
  4.           public void mouseMoved(MouseEvent e) { 
  5.               System.out.println(e.getX() + ", " + e.getY()); 
  6.           } 
  7.  
  8.       }); 
  9.  
  10.       frame.addWindowListener(new WindowAdapter() { 
  11.  
  12.           @Override 
  13.           public void windowClosing(WindowEvent e) { 
  14.               System.out.println("windowClosing"); 
  15.               System.exit(0); 
  16.           } 
  17.  
  18.           @Override 
  19.           public void windowClosed(WindowEvent e) { 
  20.               System.out.println("window closed."); 
  21.               super.windowClosed(e); 
  22.           } 
  23.  
  24.       }); 
  25.       frame.setSize(new Dimension(5001000)); 
  26.       frame.setVisible(true);