`
isiqi
  • 浏览: 16591899 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

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

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

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

  在上面讨论的接口:

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

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

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

  类适配器(ClassAdapter)VS对象适配器(ObjectAdapter)

  适配器总体上可以分为两类??类适配器(ClassAdapter)VS对象适配器(ObjectAdapter)

 类适配器:


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

 对象适配器:

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

  下表是类适配器(ClassAdapter)和对象适配器(ObjectAdapter)的详细不同:

  补充:

  类适配器(ClassAdapter)对象适配器(ObjectAdapter)

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

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

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

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

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

  在JAVA应用程序中:

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

  例子:

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

  让我们定义一个Customer类:

Customer



Figure20.1:CustomerClass
Listing20.1:CustomerClass
  1. classCustomer{
  2. publicstaticfinalStringUS="US";
  3. publicstaticfinalStringCANADA="Canada";
  4. privateStringaddress;
  5. privateStringname;
  6. privateStringzip,state,type;
  7. publicbooleanisValidAddress(){
  8. }
  9. publicCustomer(Stringinp_name,Stringinp_address,
  10. Stringinp_zip,Stringinp_state,
  11. Stringinp_type){
  12. name=inp_name;
  13. address=inp_address;
  14. zip=inp_zip;
  15. state=inp_state;
  16. type=inp_type;
  17. }
  18. }//endofclass
  不同的客户对象创建Customer对象并调用(invoke)isValidAddress方法验证客户地址的有效性。为了验证客户地址的有效性,Customer类期望利用一个地址验证类(addressvalidatorclass),这个验证类提供了在接口AddressValidator中声明的接口。

  Listing20.2:AddressValidatorasanInterface
  1. publicinterfaceAddressValidator{
  2. publicbooleanisValidAddress(Stringinp_address,
  3. Stringinp_zip,Stringinp_state);
  4. }//endofclass

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

  Listing20.3:USAddressClass
  1. classUSAddressimplementsAddressValidator{
  2. publicbooleanisValidAddress(Stringinp_address,
  3. Stringinp_zip,Stringinp_state){
  4. if(inp_address.trim().length()<10)
  5. returnfalse;
  6. if(inp_zip.trim().length()<5)
  7. returnfalse;
  8. if(inp_zip.trim().length()>10)
  9. returnfalse;
  10. if(inp_state.trim().length()!=2)
  11. returnfalse;
  12. returntrue;
  13. }
  14. }//endofclass

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

  Listing20.4:CustomerClassUsingtheUSAddressClass
  1. classCustomer{
  2. publicbooleanisValidAddress(){
  3. //getanappropriateaddressvalidator
  4. AddressValidatorvalidator=getValidator(type);
  5. //Polymorphiccalltovalidatetheaddress
  6. returnvalidator.isValidAddress(address,zip,state);
  7. }
  8. privateAddressValidatorgetValidator(StringcustType){
  9. AddressValidatorvalidator=null;
  10. if(custType.equals(Customer.US)){
  11. validator=newUSAddress();
  12. }
  13. returnvalidator;
  14. }
  15. }//endofclass


Figure20.2:Customer/USAddressValidator?ClassAssociation

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

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

  Listing20.5:CAAdressClasswithIncompatibleInterface

  1. classCAAddress{
  2. publicbooleanisValidCanadianAddr(Stringinp_address,
  3. Stringinp_pcode,Stringinp_prvnc){
  4. if(inp_address.trim().length()<15)
  5. returnfalse;
  6. if(inp_pcode.trim().length()!=6)
  7. returnfalse;
  8. if(inp_prvnc.trim().length()<6)
  9. returnfalse;
  10. returntrue;
  11. }
  12. }//endofclass


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

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

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


  Figure20.3:ClassAdapterfortheCAAddressClass
Listing20.6:CAAddressAdapterasaClassAdapter

  1. publicclassCAAddressAdapterextendsCAAddress
  2. implementsAddressValidator{
  3. publicbooleanisValidAddress(Stringinp_address,
  4. Stringinp_zip,Stringinp_state){
  5. returnisValidCanadianAddr(inp_address,inp_zip,
  6. inp_state);
  7. }
  8. }//endofclass


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

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

Listing20.7:CustomerClassUsingtheCAAddressAdapterClass

  1. classCustomer{
  2. publicbooleanisValidAddress(){
  3. //getanappropriateaddressvalidator
  4. AddressValidatorvalidator=getValidator(type);
  5. //Polymorphiccalltovalidatetheaddress
  6. returnvalidator.isValidAddress(address,zip,state);
  7. }
  8. privateAddressValidatorgetValidator(StringcustType){
  9. AddressValidatorvalidator=null;
  10. if(custType.equals(Customer.US)){
  11. validator=newUSAddress();
  12. }
  13. if(type.equals(Customer.CANADA)){
  14. validator=newCAAddressAdapter();
  15. }
  16. returnvalidator;
  17. }
  18. }//endofclass

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


  Figure20.4:AddressValidationApplication?UsingClassAdapter


  Figure20.5:AddressValidationMessageFlow?UsingClassAdapter

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

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

  1. Listing20.8:AddressValidatorasanAbstractClass
  2. publicabstractclassAddressValidator{
  3. publicabstractbooleanisValidAddress(Stringinp_address,
  4. Stringinp_zip,Stringinp_state);
  5. }//endofclass
  6. Listing20.9:CAAddressAdapterClass
  7. classCAAddressAdapterextendsAddressValidator{
  8. publicCAAddressAdapter(CAAddressaddress){
  9. objCAAddress=address;
  10. }
  11. publicbooleanisValidAddress(Stringinp_address,
  12. Stringinp_zip,Stringinp_state){
  13. }
  14. }//endofclass


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

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

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

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


  Figure20.6:ObjectAdapterfortheCAAddressClass

  Listing20.10:CAAddressAdapterasanObjectAdapter

  1. classCAAddressAdapterextendsAddressValidator{
  2. privateCAAddressobjCAAddress;
  3. publicCAAddressAdapter(CAAddressaddress){
  4. objCAAddress=address;
  5. }
  6. publicbooleanisValidAddress(Stringinp_address,
  7. Stringinp_zip,Stringinp_state){
  8. returnobjCAAddress.isValidCanadianAddr(inp_address,
  9. inp_zip,inp_state);
  10. }
  11. }//endofclass


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



  Figure20.7:AddressValidationApplication?UsingObjectAdapter

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



  Figure20.8:AddressValidationMessageFlow?UsingObjectAdapter

分享到:
评论

相关推荐

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

    适配器模式是一种结构型设计模式,允许不兼容的接口之间进行交互。在Java中,适配器模式主要通过接口或者抽象类来实现,以达到在不修改现有代码的基础上,使得原本无法一起工作的类协同工作。 适配器模式主要有两种...

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

    《深入浅出Java设计模式》是一本专注于Java编程领域中的设计模式专著,旨在帮助开发者深化对设计模式的理解,提高代码质量和可维护性。设计模式是软件开发中经过时间检验的解决方案,它们是解决常见问题的最佳实践。...

    深入浅出设计模式附书源码Java版源码

    总的来说,深入浅出设计模式附书源码Java版源代码是一份宝贵的资源,对于想要提升Java设计能力的开发者来说,它提供了一个实践和学习设计模式的绝佳平台。通过实际操作和调试代码,你可以更好地理解和掌握这些模式,...

    深入浅出设计模式之适配器模式与外观模式

    ### 深入浅出设计模式之适配器模式与外观模式 在软件工程领域,设计模式被广泛地应用于解决常见的编程问题。其中,《Head First 设计模式》是一本非常受欢迎的经典著作,它通过生动有趣的例子帮助读者理解并掌握...

    深入浅出设计模式C#Java版

    本资源"深入浅出设计模式C#Java版"是针对这两种主流编程语言介绍设计模式的经典著作,旨在帮助开发者理解和应用这些模式。 1. **单例模式**:确保一个类只有一个实例,并提供全局访问点。在C#中,可以使用`密封`和`...

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

    《深入浅出设计模式》是一本面向Java开发者的专业书籍,旨在帮助读者理解并熟练应用设计模式。设计模式是软件工程中的重要概念,它总结了在特定情境下解决常见问题的最佳实践,是经验丰富的开发者们智慧的结晶。这...

    深入浅出设计模式(HEAD_FIRST设计模式)电子书+java代码

    总结来说,《深入浅出设计模式》是一本以易懂的方式介绍设计模式的优秀教材,结合书中给出的Java代码,无论你是Java开发者还是其他语言的程序员,都能从中受益,掌握设计模式这一核心的软件工程技能。通过学习本书,...

    深入浅出设计模式(源代码c# 和JAVA)

    《深入浅出设计模式》是一本旨在帮助开发者理解和应用设计模式的经典书籍,它提供了C#和JAVA两种语言的源代码实现,使得学习者可以通过实际操作来加深对设计模式的理解。 1. **设计模式的基本概念**: 设计模式...

    深入浅出JAVASwing程序设计

    本资源“深入浅出JAVASwing程序设计”旨在帮助开发者掌握Swing的基本概念、组件用法以及高级特性,从而能创建功能强大、交互性强的Java应用。 Swing在Java AWT(Abstract Window Toolkit)的基础上构建,提供了更轻...

    Java设计模式之禅

    《Java设计模式之禅》是一本深入浅出讲解设计模式的书籍,书中不仅包含23种经典设计模式的案例,还详细介绍了设计模式背后的思想和原则,适合初学者以及对设计模式有一定了解的程序员阅读。本书旨在帮助读者理解如何...

    深入浅出设计模式源码 java

    《深入浅出设计模式源码 Java》是一本深入解析Java设计模式的著作,它通过易于理解的方式揭示了软件设计中的核心原则和模式。设计模式是软件工程中的宝贵经验总结,它们是解决常见问题的最佳实践,可以帮助开发者...

    深入浅出Java 23种设计模式.rar

    本资料"深入浅出Java 23种设计模式"旨在帮助开发者理解和应用这23种经典设计模式。 首先,我们要了解设计模式的三大类别:创建型模式、结构型模式和行为型模式。创建型模式关注对象的创建过程,如单例模式...

    Java设计模式相关书籍

    以下是对标题"Java设计模式相关书籍"及描述"设计模式之禅 深入浅出"中所包含的关键知识点的详细阐述。 1. **设计模式的分类** 设计模式通常分为三大类:创建型模式(Creational Patterns)、结构型模式...

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

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

    深入浅出设计模式 (希望对设计模式有所了解和深入研究的都适用)

    《深入浅出设计模式》是一本旨在帮助读者理解和掌握设计模式的书籍,适合那些希望深入研究设计模式的IT从业者。这本书可能与《HEAD FIRST设计模式》相辅相成,通过不同的讲解方式来帮助读者更全面地理解这一领域。 ...

    深入浅出设计模式样章

    《深入浅出设计模式样章》是一本专为软件开发者准备的指南,旨在帮助读者理解和掌握设计模式这一核心编程概念。设计模式是软件工程中经过时间验证、在特定情境下解决常见问题的有效方法,它提供了可重用的解决方案,...

Global site tag (gtag.js) - Google Analytics