`

深入浅出Java设计模式之适配器模式

阅读更多

      通常,客户类(clients of class)通过类的接口访问它提供的服务。有时,现有的类(existing class)可以提供客户类的功能 需要,但是它所提供的接口不一定是客户类所期望的。这是由于现有的接口太详细或者缺乏详细或接口的名称与客户类所查找的不同等诸多不同原因导致的。

   在这种情况下,现有的接口需要转化(convert)为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所 提供的功能。适配器模式(Adapter Pattern)可以完成这样的转化。适配器模式建议定义一个包装类,包装有不兼容接口的对象。这个包装类指的 就是适配器(Adapter),它包装的对象就是适配者(Adaptee)。适配器提供客户类需要的接口,适配器接口的实现是把客户类的请求转化为对适配 者的相应接口的调用。换句话说:当客户类调用适配器的方法时,在适配器类的内部调用适配者类的方法,这个过程对客户类是透明的,客户类并不直接访问适配者 类。因此,适配器可以使由于借口不兼容而不能交互的类可以一起工作(work together)。

  在上面讨论的接口:

  (1)    不是指在JAVA编程语言中接口的概念,虽然类的接口可以通过JAVA借扩来定义。

  (2)    不是指由窗体和GUI控件所组成的GUI应用程序的用户接口。

  (3)    而是指类所报漏的,被其他类调用的编程接口,

  类适配器(Class Adapter)VS对象适配器(Object Adapter)

  适配器总体上可以分为两类??类适配器(Class Adapter)VS对象适配器(Object Adapter)

 类适配器:


  类适配器是通过继承类适配者类(Adaptee Class)实现的,另外类适配器实现客户类所需要的接口。当客户对象调用适配器类方法的时候,适配器内部调用它所继承的适配者的方法。

    

 对象适配器:

  对象适配器包含一个适配器者的引用(reference),与类适配器相同,对象适配器也实现了客户类需要的接口。当客户对象调用对象适配器的方法的时候,对象适配器调它所包含的适配器者实例的适当方法。

  下表是类适配器(Class Adapter)和对象适配器(Object Adapter)的详细不同:

  补充:

  类适配器(Class Adapter)    对象适配器(Object Adapter)

  基于继承概念    利用对象合成

  只能应用在适配者是接口,不能利用它子类的接口,当类适配器建立时,它就静态地与适配者关联    可以应用在适配者是接口和它的所有子类,因为适配器是作为适配者的子类,所以适配器可能会重载适配者的一些行为。

  注意:在JAVA中,子类不能重载父类中声明为final的方法。    不能重载适配者的方法。

  注意:字面上,不能重栽只是因为没有继承。但是适配器提供包装方法可以按需要改变行为。

  客户类对适配者中声明为public的接口是可见的,    客户类和适配者是完全不关联的,只有适配器才能感知适配者接口。

  在JAVA应用程序中:

  适用于期待的接口是JAVA接口的形式,而不是抽象地或具体地类的形式。这是因为JAVA编程语言只允许单继承。因此,类适配器设计成适配者的子类。    在JAVA应用程序中:
适用于当客户对象期望的接口是抽象类的形式,同时也可以应用于期望接口是Java接口的形式。
    
  例子:

  让我们建立一个验证给定客户地址的应用。这个应用是作为大的客户数据管理应用的一部分。

  让我们定义一个Customer类:

Customer



Figure 20.1: Customer Class 
Listing 20.1: Customer Class

  1. class  Customer { 
  2.   public  static  final  String  US = "US"
  3.   public  static  final  String  CANADA = "Canada"
  4.   private  String  address; 
  5.   private  String  name; 
  6.   private  String  zip, state, type; 
  7.   public  boolean  isValidAddress() { 
  8.           … 
  9.           … 
  10.   } 
  11.   public  Customer(String  inp_name, String  inp_address, 
  12.                   String  inp_zip, String  inp_state, 
  13.                   String  inp_type) { 
  14.     name = inp_name; 
  15.     address = inp_address; 
  16.     zip = inp_zip; 
  17.     state = inp_state; 
  18.     type = inp_type; 
  19.   } 
  20. }//end of class 

   不同的客户对象创建Customer对象并调用(invoke)isValidAddress方法验证客户地址的有效性。为了验证客户地址的有效 性,Customer类期望利用一个地址验证类(address validator class),这个验证类提供了在接口 AddressValidator中声明的接口。

  Listing 20.2: AddressValidator as an Interface

  1. public  interface  AddressValidator { 
  2.   public  boolean  isValidAddress(String  inp_address, 
  3.      String  inp_zip, String  inp_state); 
  4. }//end of class 


  让我们定义一个USAddress的验证类,来验证给定的U.S地址。

  Listing 20.3: USAddress Class

  1. class  USAddress implements  AddressValidator { 
  2.   public  boolean  isValidAddress(String  inp_address, 
  3.      String  inp_zip, String  inp_state) { 
  4.    if  (inp_address.trim().length () < 10) 
  5.      return  false
  6.    if  (inp_zip.trim().length () < 5) 
  7.      return  false
  8.    if  (inp_zip.trim().length () > 10) 
  9.      return  false
  10.    if  (inp_state.trim().length () != 2) 
  11.      return  false
  12.    return  true
  13.   } 
  14. }//end of class 


  USAddress类实现AddressValidator接口,因此Customer对象使用USAddress实例作为验证客户地址过程的一部分是没有任何问题的。

  Listing 20.4: Customer Class Using the USAddress Class

  1. class  Customer { 
  2.           … 
  3.           … 
  4.  public  boolean  isValidAddress() { 
  5.    //get an appropriate address validator 
  6.    AddressValidator validator = getValidator(type); 
  7.    //Polymorphic call to validate the address 
  8.    return  validator.isValidAddress(address, zip, state); 
  9.  } 
  10.  private  AddressValidator getValidator(String  custType) { 
  11.    AddressValidator validator = null
  12.    if  (custType.equals(Customer.US)) { 
  13.      validator = new  USAddress(); 
  14.    } 
  15.    return  validator; 
  16.  } 
  17. }//end of class 

 


Figure 20.2: Customer/USAddress Validator?Class Association 

  但是当验证来自加拿大的客户时,就要对应用进行改进。这需要一个验证加拿大客户地址的验证类。让我们假设已经存在一个用来验证加拿大客户地址的使用工具类CAAddress。

 

从下面的CAAdress类的实现,可以发现CAAdress提供了客户类Customer类所需要的验证服务。但是它所提供的接口不用于客户类Customer所期望的。

  Listing 20.5: CAAdress Class with Incompatible Interface

  1. class  CAAddress { 
  2.   public  boolean  isValidCanadianAddr(String  inp_address, 
  3.      String  inp_pcode, String  inp_prvnc) { 
  4.    if  (inp_address.trim().length () < 15) 
  5.      return  false
  6.    if  (inp_pcode.trim().length () != 6) 
  7.      return  false
  8.    if  (inp_prvnc.trim().length () < 6) 
  9.      return  false
  10.    return  true
  11.   } 
  12. }//end of class 


  CAAdress类提供了一个isValidCanadianAddr方法,但是Customer期望一个声明在AddressValidator接口中的isValidAddress方法。

  接口的不兼容使得Customer对象利用现有的CAAdress类是困难的。一种意见是改变CAAdress类的接口,但是可能会有其他的应用正在使用CAAdress类的这种形式。改变CAAdress类接口会影响现在使用CAAdress类的客户。

  应用适配器模式,类适配器CAAdressAdapter可以继承CAAdress类实现AddressValidator接口。

 


  Figure 20.3: Class Adapter for the CAAddress Class 
Listing 20.6: CAAddressAdapter as a Class Adapter

  1. public  class  CAAddressAdapter extends  CAAddress 
  2.   implements  AddressValidator { 
  3.   public  boolean  isValidAddress(String  inp_address, 
  4.      String  inp_zip, String  inp_state) { 
  5.     return  isValidCanadianAddr(inp_address, inp_zip, 
  6.            inp_state); 
  7.   } 
  8. }//end of class 


   因为适配器CAAdressAdapter实现了AddressValidator接口,客户端对象访问适配器CAAdressAdapter对象是没 有任何问题的。当客户对象调用适配器实例的isValidAddress方法的时候,适配器在内部把调用传递给它继承的 isValidCanadianAddr方法。

  在Customer类内部,getValidator私有方法需要扩展,以至于它可以 在验证加拿大客户的时候返回一个CAAdressAdapter实例。返回的对象是多态的,USAddress和CAAddressAdapter都实现 了AddressValidator接口,所以不用改变。

Listing 20.7: Customer Class Using the CAAddressAdapter Class

  1. class  Customer { 
  2.           … 
  3.           … 
  4.   public  boolean  isValidAddress() { 
  5.     //get an appropriate address validator 
  6.     AddressValidator validator = getValidator(type); 
  7.     //Polymorphic call to validate the address 
  8.     return  validator.isValidAddress(address, zip, state); 
  9.   } 
  10.   private  AddressValidator getValidator(String  custType) { 
  11.     AddressValidator validator = null
  12.     if  (custType.equals(Customer.US)) { 
  13.       validator = new  USAddress(); 
  14.     } 
  15.     if  (type.equals(Customer.CANADA)) { 
  16.       validator = new  CAAddressAdapter(); 
  17.     } 
  18.     return  validator; 
  19.   } 
  20. }//end of class 

  CAAddressAdapter设计和对AddressValidator(声明期望的接口)对象的多态调用使Customer可以利用接口不兼容CAAddress类提供的服务。

 


  Figure 20.4: Address Validation Application?Using Class Adapter

 


  Figure 20.5: Address Validation Message Flow?Using Class Adapter 

  作为对象适配器的地址适配器

   当讨论以类适配器来实现地址适配器时,我们说客户类期望的AddressValidator接口是Java接口形式。现在,让我们假设客户类期望 AddressValidator接口是抽象类而不是java接口。因为适配器CAAdapter必须提供抽象类AddressValidatro中声明 的接口,适配器必须是AddressValidator抽象类的子类、实现抽象方法。

  1. Listing 20.8: AddressValidator as an Abstract Class  
  2. public  abstract  class  AddressValidator { 
  3.   public  abstract  boolean  isValidAddress(String  inp_address, 
  4.      String  inp_zip, String  inp_state); 
  5. }//end of class 
  6. Listing 20.9: CAAddressAdapter Class  
  7. class  CAAddressAdapter extends  AddressValidator { 
  8.           … 
  9.           … 
  10.   public  CAAddressAdapter(CAAddress address) { 
  11.     objCAAddress = address; 
  12.   } 
  13.   public  boolean  isValidAddress(String  inp_address, 
  14.      String  inp_zip, String  inp_state) { 
  15.           … 
  16.           … 
  17.   } 
  18. }//end of class 


  因为多继承在JAVA中不支持,现在适配器CAAddressAdapter不能继承现有的CAAddress类,它已经使用了唯一一次继承其他类的机会。

  应用对象适配器模式,CAAddressAdapter可以包含一个适配者CAAddress的一个实例。当适配器第一次创建的时候,这个适配者的实例通过客户端传递给适配器。通常,适配者实例可以通过下面两种方式提供给包装它的适配器。

  (1)    对象适配器的客户端可以传递一个适配者的实例给适配器。这种方式在选择类的形式上有很大的灵活性,但是客户端感知了适配者或者适配过程。这种方法在适配器不但需要适配者对象行为而且需要特定状态时很适合。

  (2)    适配器可以自己创建适配者实例。这种方法相对来说缺乏灵活性。适用于适配器只需要适配者对象的行为而不需要适配者对象的特定状态的情况。

 


  Figure 20.6: Object Adapter for the CAAddress Class 

  Listing 20.10: CAAddressAdapter as an Object Adapter

  1. class  CAAddressAdapter extends  AddressValidator { 
  2.   private  CAAddress objCAAddress; 
  3.   public  CAAddressAdapter(CAAddress address) { 
  4.     objCAAddress = address; 
  5.   } 
  6.   public  boolean  isValidAddress(String  inp_address, 
  7.      String  inp_zip, String  inp_state) { 
  8.     return  objCAAddress.isValidCanadianAddr(inp_address, 
  9.            inp_zip, inp_state); 
  10.   } 
  11. }//end of class 


  当客户对象调用CAAddressAdapter(adapter)上的isValidAddress方法时, 适配器在内部调用CAAddress(adaptee)上的isValidCanadianAddr方法。

 


  Figure 20.7: Address Validation Application?Using Object Adapter 

  从这个例子可以看出,适配器可以使Customer(client)类访问借口不兼容的CAAddress(adaptee)所提供的服务!

 



  Figure 20.8: Address Validation Message Flow?Using Object Adapter

 

原文地址:http://dev.yesky.com/84/2168584_2.shtml

分享到:
评论

相关推荐

    深入浅出Java设计模式之适配器模式[借鉴].pdf

    深入浅出Java设计模式之适配器模式[借鉴].pdf

    深入浅出java设计模式(高清中文PDF)

    文件类型为PDF文件,此文档对20多种java设计模式进行了详细讲解,在中文讲解的过程中还附有代码示例给学习者进行参考,使学习者通过实践更容易理解设计模式的原理。 本文档目录: 1.工厂模式 2.单例模式 3.建造...

    深入浅出设计模式(中文版)

    1.1什么是设计模式 2 1.2设计模式的作用 3 1.3GRASP模式的分类 4 1.4GoF设计模式的分类 4 1.5模式的学习阶段 6 第2章负责任地设计对象——GRASP 9 2.1InformationExpert(信息专家) 11 2.2Creator(创造者)...

    深入浅出设计模式(中文版电子版)

    1.1什么是设计模式 2 1.2设计模式的作用 3 1.3GRASP模式的分类 4 1.4GoF设计模式的分类 4 1.5模式的学习阶段 6 第2章负责任地设计对象——GRASP 9 2.1InformationExpert(信息专家) 11 2.2Creator(创造者)...

    Java设计模式 版本2

    Java设计模式,目录:前言,UML建模技术,深入浅出UML类图,从招式与内功谈起——设计模式概述,面向对象设计原则,工厂三兄弟之简单工厂模式,工厂三兄弟之工厂方法模式,工厂三兄弟之抽象工厂模式,确保对象的唯一...

    design-pattern-java.pdf

    扩展系统功能——装饰模式(三) 扩展系统功能——装饰模式(四) 外观模式-Facade Pattern 深入浅出外观模式(一) 深入浅出外观模式(二) 深入浅出外观模式(三) 享元模式-Flyweight Pattern 实现对象的复用——...

    Head First Design Pattern(en) pdf(part3)

    1 欢迎来到设计模式世界:设计模式入门  模拟鸭子应用  Joe想到继承  利用接口如何?  软件开发的不变真理  分开变化和不变部分  设计鸭子的行为  测试鸭子的代码  动态地设置行为  封装行为的大局观  ...

    Head First Design Pattern(en) pdf(part1)

    1 欢迎来到设计模式世界:设计模式入门  模拟鸭子应用  Joe想到继承  利用接口如何?  软件开发的不变真理  分开变化和不变部分  设计鸭子的行为  测试鸭子的代码  动态地设置行为  封装行为的大局观  ...

    asp.net知识库

    深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托...

Global site tag (gtag.js) - Google Analytics