Java与模式:适配器模式

目的 
将一个类的接口转换成客户希望的另外一个接口。A d a p t e r 模式使得原本由于接口

不兼容而不能一起工作的那些类可以一起工作。

-----  尽量优先使用Object Adapter的模式

意图: 
你想使用一个已经存在的类,而它的接口不符合你的需求。 
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。 
(仅适用于对象A d a p t e r )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。 
(目的和意图是不变的,摘自《设计模式迷你手册》电子版)

原理:

保留现有的类所提供的服务,修改其接口,从而达到客户端的期望。

特征:

目标(Target)角色:所期望得到的接口。 
    
适配源(Adaptee):现在的需要适配的接口。 
    
适配器(Adapter):将适配源接口适配成目标接口。 
    
类适配器使用继承关系复用适配源(Adaptee),因此目标(Target)不能是类,只是能接口(java单继承)。 
    
对象适配器使用委派关系复用适配源(Adaptee),因此目标(Target)可能是类或接口,可以将多个适配源适配到一个目标接口。

Java与模式:适配器模式 - Hunter - 一生有你

 

Java与模式:适配器模式 - Hunter - 一生有你

 一、原理图:

Java与模式:适配器模式 - Hunter - 一生有你

二、示例代码

  1. /** 
  2. * Created by IntelliJ IDEA. 
  3. * User: leizhimin 
  4. * Date: 2008-8-2 14:43:46 
  5. 源角色 
  6. */  
  7. public class Adaptee {  
  8.     public int get220v(){  
  9.        return 220;  
  10.     }  
  11. }  
  12.    
  13. /** 
  14. * Created by IntelliJ IDEA. 
  15. * User: leizhimin 
  16. * Date: 2008-8-2 14:43:23 
  17. 目标角色 
  18. */  
  19. public interface Target {  
  20.     int get110v();  
  21.     int get220v();  
  22. }  
  23.    
  24. /** 
  25. * Created by IntelliJ IDEA. 
  26. * User: leizhimin 
  27. * Date: 2008-8-2 14:43:07 
  28. 适配器角色:扩展源角色,实现目标角色,从而使得目标角色改动时候,不用改动源角色,只要改动适配器 
  29. */  
  30. public class Adapter extends Adaptee implements Target{  
  31.     public int get110v(){  
  32.         return 110;  
  33.     }  
  34. }  
  35.    
  36. /** 
  37. * Created by IntelliJ IDEA. 
  38. * User: leizhimin 
  39. * Date: 2008-8-2 15:00:31 
  40. 客户端 
  41. */  
  42. public class Client {  
  43.     public static void main(String rags[]) {  
  44.         new Client().test();  
  45.     }  
  46.   
  47.     public void test() {  
  48.         Target target = new Adapter();  
  49.         int v1 = target.get110v();  
  50.         int v2 = target.get220v();  
  51.     }  
  52. }  

1.概念: 
适配器模式(Adapter Pattern[GOF95]把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。 
2.
两种形式 
a.
的适配器模式 b.对象的适配器模式 
3.
模拟问题: 
   
现在假设我们的程序已经设计了接口Request接口,但是现在有一个特殊的接口SpecificRequst能更好的完成我们的功能,但是它和我们现有的Request接口不匹配。那我们如何将它们一起工作呢?看下面的实例: 
3.
图示实例1a.的适配器模式

 

目标角色:

 public interface Target {  

?      public void request();  

?  } 

源角色:

public class Adaptee {  

    public void specificRequest(){  

        System.out.println("实现所需功能");  

    }  

}  

适配器角色:

public class ClassAdapter extends Adaptee implements Target {  

    public void request() {  

        this.specificRequest();  

    }  

}  

用户角色:

public class TestClassAdapter {  

    public static void main(String args[]){  

        ClassAdapter adapter = new ClassAdapter();  

        adapter.request();  

    }  

}  

运行结果:

引用

实现所需功能

 

3.图示实例2b.对象的适配器模式

实例代码: 
目标角色,源角色代码不变

适配器角色:

public class ObjectAdapter implements Target {  

    private Adaptee adaptee;  

    public ObjectAdapter(Adaptee adaptee){  

        this.adaptee = adaptee;  

    }  

    public void request() {  

        adaptee.specificRequest();  

    }  

}  

用户角色:

public class TestOjbectAdapter {  

    public static void main(String arg[]){  

        Adaptee adaptee = new Adaptee();  

        ObjectAdapter adapter = new ObjectAdapter(adaptee);  

        adapter.request();  

    }  

}  

    将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。  --- 《设计模式》GOF

Adapter模式的宗旨就是,基于现有类所提供的服务,向客户端提供接口,以满足客户的期望。---java设计模式 

 

对软件系统中,如果要复用以前的现存对象,但是这些对象所提供的接口并不一定能适应我们的新环境,我们就要将其转换成我们需要的接口,来供我们调用。 Adapter模式通过定义一个新的接口(对要实现的功能加以抽象),和一个实现该接口的Adapter(适配器)类来透明地调用外部组件。这样替换外部 组件时,最多只要修改几个Adapter类就可以了,其他源代码都不会受到影响。

简单理解就是:我们需要实现某个功能,而现在实现这个功能的组件不必我们自己开发,可以通过第三方的组件(即别人的代码或者自己曾经写过的代码)来实现, 但第三方组件的接口与现在所定义的接口不一致(即类名,方法名不一样),那么在不修改两方接口的情况下,可以通过采用适配器模式来解决这一问题。

Target 为用户请求接口

      适配器的UML图:

Java与模式:适配器模式 - Hunter - 一生有你

 

Target       即客户端给出的接口(此处不是java语言中的接口类型,而是指类名,方法名等等),也就是客户端需要调用的组件。
 Adapter     
即适配器

Adaptee     即第三方组件

 

根据Target是否是java接口类型,适配器可以分为类适配器对象适配器

 

类适配器: 
        
此时Target是一个java接口,其中定义了其所期望的功能,而此时的Adapter则通过继承Adaptee类并实现Target接口来完成。即

  1. class Adapter extends Adaptee implements Target{}  

假设现在Target的内容为

  1.  interface operation {  
  2.       public int add(int a , int b);     -à 返回类型为整形
  3. }  

Adaptee的内容为:

  1. public class Adaptee{  
  2.     public int addOpe(int a ,int b){  
  3.          return a+b;  
  4.     }  
  5. }  

那么为了利用Adaptee类,类适配器Adapter可以写成:

  1.  public class Adapter extends Adaptee implements Target{  
  2. ?       public int add(int a , int b) {  
  3. ?          return addOpe(a,b);  
  4. ?       }  
  5. ?  } 

对象适配器:

此时Target可能是一个普通类,那么Adapter的实现可以通过继承Target,并将具体实现委托给Adaptee来完成。同样是前面的add例子:

1.       class Target{  

2.           public int add(int a , int b){}  

3.   

4.   

5.       Public class Adapter extends Target{  

6.           Adaptee adaptee;  

7.           Adapter(Adaptee adaptee){  

8.              this.adaptee = adaptee ;  

9.           }  

10.       public int add(int a,int b){  

11.          return adaptee.addOpe(a,b);  

12.       }  

13.   }  

 

总结:个人觉得适配器模式用到了面向对象语言中的多态的特性,根据客户端给出的组件是接口还是类,适配器通过实现接口或者继承类的方式来实现多态。 如果是实现接口,那么适配器则可以继承第三方组件,通过调用父类方法来完成功能。如果是继承,因为java中不支持多继承,适配器将具体操作委派给第三方 组件来完成。这是根据客户端给出的组件类型来区分,个人认为即使客户端给出的组件是接口,也可以通过将具体实现委派给第三方组件来完成,因为设计模式的原 则是:优先使用对象组合而不是类继承。这样适配器模式就很容易理解,就是客户端通过多态调用适配器,适配器通过使用第三方对象来完成具体功能。

 

适配器模式(Adapter Pattern)(另称-变压器模式):

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

1(类适配器)模式所涉及的角色有
1
/目标(Target)角色:这就是所期待得到的接口。由于是类适配器模式,因此目标不可以是类。 
2
/源(Adaptee)角色:现有需要适配的接口。 
3
/适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

  1. //目标角色类  
  2.         public interface Target{  
  3.             //源类有的方法  
  4.             void sampleOperation1();  
  5.             //源类没有的方法  
  6.             void sampleOperation2();  
  7.         }  
  8.         源类(具体类)  
  9.         public class Adaptee{  
  10.             //源类含有的方法sampleOperation1()  
  11.             public void sampleOperation1(){}  
  12.         }  
  13.         //适配器角色  
  14.         public class Adapter extends Adaptee implements Target{  
  15.             public void sampleOperation2(){}  

 

2(对象适配器) 
1
):模式所涉及的角色有: 
1
/目标(Target)角色:这就是所期待的接口,目标可以是具体的或抽象的类 
2
/源(Adaptee)角色:现有需要适配的接口 
3
/适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口,这一角色必须是具体类        }  

 

1.       //Target  

2.                   public interface Target{  

3.                       //源类有的方法  

4.                       void sampleOperation1();  

5.                       //源来没有的方法  

6.                       void sampleOperation2();  

7.                   }  

8.                   源类(具体类)  

9.                   public class Adaptee{  

10.                   //源类含有的方法sampleOperation1()  

11.                   public void sampleOperation1(){}  

12.               }  

13.               //适配器类  

14.               public class Adapter implements Target{  

15.                   private Adaptee adaptee;  

16.                     

17.                   public Adapter(Adaptee adaptee){  

18.                       super();  

19.                       this.adaptee = adaptee;  

20.                   }  

21.                   //源类有的方法,适配器直接委派就可以了  

22.                   public void sampleOperation1(){  

23.                       adaptee.sampleOperation();  

24.                   }  

25.                   //源类没有,需要补充  

26.                   public void sampleOperation2(){  

27.                       //............  

28.                   }  

29.               }  

适配器模式的用意是将接口不同而功能相同或者相近的两个接口加以转换,这里面包括适配器角色补充了一个源角色没有的方法。 

4
、对象适配器模式的效果 
1
)一个适配器可以把多种不同的源适配到同一个目标,换言之,同一个适配器可以把源类和它的子类都适配到目标接口。 
2
)与类的适配器模式相比,要想置换源类的方法就不容易。如果一定要置换掉源类的一个或多个方法,就只好先做一个源 
   
类的子类,将源类的方法置换掉,然后再把原来的子类当做真正的源进行适配。 
3
)虽然要想置换源类的方法不容易,但是要想增加一些新的方法则方便的很,而且新增加的方法可同时适用于所有的源。 
5
在什么情况下使用适配器模式 
1
)系统需要使用现有的类,而此类的接口不符合系统的需要。 
2
)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。 
   
这些源类不一定有很复杂的接口。 
3
)(对对象适配器模式而言)在设计里,需要改变多个已有的子类的接口,如果使用类的适配器模式,就要针对每一个子类做 
   
一个适配器,而这不太实际

 

  1. //Itermeration  
  2.          import java.util.Iterator;  
  3.          import java.util.*;  
  4.          import java.util.Enumeration;  
  5.            
  6.          public class Itermeration implements Enumeration{  
  7.             private Iterator it;  
  8.               
  9.             public Itermeration(Iterator it){  
  10.                 this.it = it;  
  11.                   
  12.                 //是否存在下一个元素  
  13.                 public boolean hasMoreElements(){  
  14.                     return it.hasNext();  
  15.                 }  
  16.                   
  17.                 //返还下一个元素  
  18.                 public Object nextElement() throws NoSuchElementException{  
  19.                     return it.next();  
  20.                 }  
  21.             }  
  22.          }  
  23.            
  24.          //Enuterator  
  25.          import java.util.Iterator;  
  26.          import java.util.*;  
  27.          import java.util.Enumeration;  
  28.            
  29.          public class Enuterator implements Iterator{  
  30.             Enumeration enum;  
  31.               
  32.             public Enuterator(Enumeration enum){  
  33.                 this.enum = enum;  
  34.             }  
  35.               
  36.             //是否存在下一个元素  
  37.             public boolean hasNext(){  
  38.                 return enum.hasMoreElements();  
  39.             }  
  40.               
  41.             //返还下一个元素  
  42.             public Object next() throws NoSuchElementsException{  
  43.                 return enum.nextElement();  
  44.             }  
  45.               
  46.             //删除当前的元素(不支持)  
  47.             public void remove(){  
  48.                 throw new UnsupportedOperationException();  
  49.             }  
  50.          }  
  51.          --------------------------------------------------  
  52.           
  53.         //立方体类  
  54.         public class Cube{  
  55.             private double width;  
  56.               
  57.             public Cube(double width){  
  58.                 this.width = width;  
  59.             }  
  60.               
  61.             //计算体积  
  62.             public double calculateVolume(){  
  63.                 return width*width*width;  
  64.             }  
  65.               
  66.             //计算面积  
  67.             public double calculateFaceArea(){  
  68.                 return width*width;  
  69.             }  
  70.               
  71.             //长度的取值方法  
  72.             public double getWidth(){  
  73.                 return this.width;  
  74.             }  
  75.               
  76.             //长度的赋值方法  
  77.             public void setWidth(double width){  
  78.                 this.width = width;  
  79.             }  
  80.         }  
  81.           
  82.         //目标接口角色  
  83.         public interface BallIF{  
  84.             //计算面积  
  85.             double calculateVolume();  
  86.             //半径的取值方法  
  87.             double getRadius();  
  88.             //半径的赋值方法  
  89.             void setRadius(double radius);  
  90.         }  
  91.           
  92.         //适配器类角色  
  93.         public class MagicFinger implements BallIF{  
  94.             private double redius = 0;  
  95.             private static final double PI = 3.14D;  
  96.             private Cube adaptee;  
  97.               
  98.             public MagicFinger(Cube adaptee){  
  99.                 super();  
  100.                 this.adaptee = adaptee;  
  101.                 radius = adpatee.getWidth();  
  102.             }  
  103.               
  104.             //计算面积  
  105.             public double calculateArea(){  
  106.                 return PI*4.0D*(radius);  
  107.             }  
  108.               
  109.             public double calculateVolume(){  
  110.                 return PI*(4.0D/3.0D)*(radius*radius*radius);  
  111.             }  
  112.               
  113.             //半径取值方法  
  114.             public double getRadius(){  
  115.                 return radius;  
  116.             }  
  117.               
  118.             public void setRadius(double radius){  
  119.                 this.radius = radius;  
  120.             }  
  121.         }  
  122.           

6、本模式在实现的时候有以下这些值得注意的地方
1) 
目标接口可以省略。此时,目标接口和源接口实际上是相同的。由于源是一个接口,而适配器类是一个类(或抽象类) 
   
因此这种做法看似平庸而并并平庸,它可以使客户端不必实现不需要的方法。 
2
)适配器类可以是抽象类,这可以在缺省适配情况下看到。 
3
)带参数的适配器模式。使用这种方法可以根据参数返还一个合适的实例给客户端 

7
、适配器模式与其他模式的关系 
1
)适配器模式与桥梁模式的关系 
桥梁模式的用意是要把实现和它的接口分开,以便它们可以独立地变化。桥梁模式并不是用来把一个已有的对象接到不相 
匹配的接口上的。当一个客户端只知道一个特定的接口,但是有必须与具有不同接口的类打交道时,就应当使用适配器模式。 
2
适配器模式装饰模式的关系 
一个装饰类也是位于客户端和另外一个Compontent对象之间的, 
在它接到客户端的调用后把调用传给一个或几个Component对象。 
一个纯粹的装饰类必须与Compotent对象在接口上的完全相同,并增强后者的功能。 
与适配器类不同的是,装饰类不能改变它所装饰的Compotent对象的接口。 
3
)适配器模式与缺省适配模式的关系

 

场景
相信很多人都知道什么是显卡,也有很多人知道显卡的本名——图形适配器。恩,是的,

正好这回说说Apater模式,就拿显卡来例子来分析一下Adapter模式

我们知道显示器(Client)是用来显示图形的,它是不能显示数据,它只能够接受来自图形发送设备Target的信号。可是我们手头上只有 CPU(Adaptee)这个产生各种描述图形的数据的数据发送器。我们需要将这些数据让显示器进行显示,可是这两个部件却是不兼容的。于是我们需要一个 中间设备,它能够将CPU“适配于显示器,这便是我们的显卡——图形适配器(Adapter)

 

·  // 图形发送设备  

?  public class Target {  

?      /** 

?       * 传送图形信号 

?       */  

?      public String request() {  

?          return "Graphic sender";  

?      }  

?  } 

 

// 显示器  

public class Client {  

  

    public static void main(String[] args) {  

        Target target = new Targete();  

        System.out.println(target.request());  

    }  

}  

 

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

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

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

  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-显卡-显示器:

  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. }  

图形发送设备    Target显示器  ClientCPU   Adaptee  ,显卡(Adapter)

 

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

 

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

  1. public class Adapter extends Targer, Adaptee {    -- 多继承(C++中合法)
  2.     ......  
  3. }  

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

1.       // IAdaptee接口  

2.       public interface IAdaptee {    

3.        

4.           String getData();  

5.       }   

 

6.       // Adaptee 实现IAdaptee 

7.       public class Adaptee implements IAdaptee {  

8.         ......  

9.  }    

 

10.   public class Adapter extends Target implements IAdaptee {  

11.     

12.       private IAdaptee apt = null;  

13.     

14.       public Adapter(IAdaptee apt) {  

15.           this.apt = apt;  

16.       }  

17.     

18.       public String request() {  

19.           return apt.getData();  

20.       }  

21.     

22.       public String getData() {  

23.           return apt.getData();  

24.       }  

25.   }  

对于我们的显示器(Client)方面,Class AdapterObject Adapter一样,所以不需要进行修改。对于Class Adapter,大家也看见了,在Adapter中因为是实现了IAdaptee接口,因此需要实现getData()的接口。一旦TargetIAdaptee拥有相同的方法时,会出现麻烦的。所以尽量优先使用Object Adapter的模式


展开阅读全文

没有更多推荐了,返回首页