`
fengzl
  • 浏览: 218046 次
  • 性别: Icon_minigender_1
  • 来自: 宁波
社区版块
存档分类
最新评论

适配器模式

阅读更多

适配器模式 <o:p> </o:p>

概述 <o:p> </o:p>

<o:p> </o:p>

这一章我们通过对适配器模式 (Adapter Pattern) 的讲解来继续我们对设计模式的学习。 Adapter 模式是一个经常使用的模式,正如你将会看到的那样,她常和其他模式一起使用。 <o:p> </o:p>

在这一章里: <o:p> </o:p>

l          我们将学习到什么是 Adapter 模式,她有什么用,以及怎么用。 <o:p> </o:p>

l          在章末的时候将会有 Adapter 模式的主要功能的总结。 <o:p> </o:p>

l          通过 Adapter 模式演示多态的使用。 <o:p> </o:p>

l          演示用 UML 图来表示不同层次的细节描述。 <o:p> </o:p>

l          我在实践过程中的一些对 Adapter 模式的观察和思考,还会将 Adapter 模式和 Facade 模式进行对比。 <o:p> </o:p>

l          <o:p> </o:p>

Adapter 模式简介 <o:p> </o:p>

<o:p> </o:p>

GoF 的经典著作《设计模式》中指出: Adapter 模式的目的就是将类的接口转换成客户对象需要的接口, Adapter 模式使原本不相容的接口变的相容。也就是说我们有一个可以满足我们需要的对象,但是她的接口却不是象我们所期望的那样,而我们现在所需要的就是创建一个新的接口,让原本的接口能够满足我们的需要。 <o:p> </o:p>

<o:p> </o:p>

Adapter 模式详解 <o:p> </o:p>

<o:p> </o:p>

要理解 Adapter 模式最简单的方式就是看看她实际应用的一个例子。假设有如下的要求: <o:p> </o:p>

l          创建一些代表着 点、线、方形 的类,并包含“ display ( 显示 ) 这个方法 / 行为。 <o:p> </o:p>

l          客户对象不需要知道正在使用的对象是 点、线 还是方形,只需要知道是一种形状 (shape) 就可以了。 <o:p> </o:p>

换句话说,我需要把这些确切的点啦、线啦、方形啦等等用更概念性的语言类概括起来,也就是 显示一个形状。 <o:p> </o:p>

当我们在完成这个例子的时候,我们可能会遇到这样的情况: <o:p> </o:p>

l          我们想用一段子程序或者说一些别人已经写好了的代码,因为这些刚好能够完成我们所需要的某些功能。 <o:p> </o:p>

l          但是,我们又不能把这些程序啦、代码啦直接放到我们自己的程序里面去, <o:p> </o:p>

l          因为这些程序、代码的接口的调用方式和我们期望的又不一样。 <o:p> </o:p>

<o:p> </o:p>

也就是说虽然当前的系统里有 点、线、方形等等这些对象,但是,我的程序仅仅需要把他们统统当成是一种形状这就足够了。具体是点、线、还是方形,我不感兴趣。 <o:p> </o:p>

l          这可以使得我的程序可以用一种方式来处理所有的“形状”,这样,就不必再去考虑这个 线 他们有什么不同之类的问题。 <o:p> </o:p>

l          这种方式还可以让我以后可以在不改变现有的使用方式的情况下添加进新的具体形状。 <o:p> </o:p>

<o:p> </o:p>

这样,我们就需要用到 多态 (polymorphism) 。就是说,我的系统里会有各种各样的具体形状,但是我可以用一个通用的方式来操纵这些所有的形状。 <o:p> </o:p>

这样,客户对象就可以简单地告诉这些 点、线 让他们在屏幕上显示出来,或者从屏幕上消失。这些 点、线 他们自己会知道自己该怎么样的去显示或不显示,不需要客户对象去考虑这个问题。 <o:p> </o:p>

<o:p> </o:p>

要达到这点,我们先创建一个 Shape 类,然后从她派生 (derive) 出一些代表着 点、线 等具体形状的类。请参看图 7-2
<o:p> </o:p>

<o:p>  </o:p>

7-2 Point Lines 、和 Square 都是一种具体的 Shape <o:p> </o:p>

<o:p> </o:p>

首先,我们必须给 Shape 类确定一些她将会提供的具体行为。我们可以通过在 Shape 里面定义一些抽象接口然后在 Point Line Square 等类中具体实现这些接口的方式来达到目的。 <o:p> </o:p>

我们给 Shape 提供以下的一些方法: <o:p> </o:p>

l          指定 Shape 在屏幕上的位置 <o:p> </o:p>

l          获取 Shape 在屏幕上的位置 <o:p> </o:p>

l          Shape 在屏幕上显示 <o:p> </o:p>

l          Shape 进行填充 <o:p> </o:p>

l          Shape 指定一个颜色 <o:p> </o:p>

l          Shape 从屏幕上消失 <o:p> </o:p>

<o:p>  </o:p>

<o:p> </o:p>

假设现在我们又被要求要去实现一个   一个新的形状 ( 要知道,客户对我们的要求总是在不停的变来变去的 ) 。这样,我们就又要创建一个新的 Circle 类了。我们让 Circle 也继承 Shape ,这样我们就也可以对 Circle 使用多态,让他向上转型 (upcasting) Shape 了。 Circle 类是建立好了,但是我们现在就需要对她的 display() fill() undisplay() 等等这些方法编码了。这好像是一个痛苦的事情。 <o:p> </o:p>

幸运的是,几经搜索 ( 就像优秀的编码人员经常做的那样 ) ,我终于找到一个替代品 (alternative) 了。我发现 Jill 同学已经写了一个名为 XXCircle 的类来处理和圆相关的东西 ( 如图 7-4) 。然而,很不幸的是, Jill 并没有象我们这样定义 XXCircle 里的方法。她定义成了: displayIt() fillIt() undisplayIt() <o:p> </o:p>

<o:p> </o:p>

7-4 Jill XXCircle <o:p> </o:p>

<o:p> </o:p>

由于我们还要用 Shape 的多态,所以我们不可能直接用 Jill XXCircle 类。至于为什么,那是因为: <o:p> </o:p>

l          不同的方法名称和参数表   XXCircle 里的方法名称和参数表和我们的 Shape 类的完全不一样。 <o:p> </o:p>

l          不是 Shape 的派生类 ( 子类 )  我们不仅仅要求方法名称参数表要一样还要求继承 Shape 类。 <o:p> </o:p>

<o:p> </o:p>

也许我们可以试着让 Jill 同学按照我们的需要来重写她的 XXCircle ,改变方法名称和参数表并且继承 Shape 类。但是,这肯定是不现实的。因为,如果重写了 XXCircle ,那么 Jill 以前用到 XXCircle 的地方也都要完全重写,而且,修改正在使用中的代码有可能会产生难以预知的副作用。 <o:p> </o:p>

<o:p> </o:p>

看起来我们就要达到目的了,然而却不能用,而我又懒得自己老老实实去写,怎么办呢? <o:p> </o:p>

<o:p> </o:p>

( 不是有句话说:“既然不能改变它,就去适应它么”?……译著 )<o:p></o:p>

<o:p> </o:p>

与其改变 XXCircle ,倒不如适配它。 <o:p> </o:p>

<o:p> </o:p>

我可以从 Shape 派生一个类,这样,这个类就可以实现 Shape 的所有接口而避免重写 XXCircle 。具体情况请看图 7-5 <o:p> </o:p>

<o:p>  </o:p>

7-5 Adapter 模式: Circle 包含 XXCircle<o:p></o:p>

l          Circle 类继承自 Shape <o:p> </o:p>

l          Circle 里包含了 XXCircle <o:p> </o:p>

l          Circle 把所有传递给 Circle 类型对象的请求全部传递给 XXCircle 类的对象。 <o:p> </o:p>

7-5 Circle XXCircle 中连线末端的菱形表明 Circle 包含得有 XXCircle 。当 Circle 类的对象初始化的时候,该对象会同时初始化一个相关的 XXCircle 类的对象。如果 XXCircle 对象包含有 Circle 所需要的所有功能,那么对 Circle 对象的任何操作都将被传递到 XXCircle 上去执行完成 (XXCircle 不包含所有 Circle 需要功能的情况随后再说 ) <o:p> </o:p>

看下面的一段代码

Example 7-1 Java Code Fragments: Implementing the Adapter Pattern<o:p></o:p>

<o:p> </o:p>

 1 class Circle extends Shape{
 2   
 3   private XXCircle myXXCircle;
 4   
 5   public Circle(){
 6     myXXCricle = new XXCircle();
 7   }
 8   
 9   public void display(){
10     myXXCircle.display();
11   }
12 }

 

通过 Adapter 我们就可以继续对 Shape 使用多态了。就是说,客户对象并不需要知道 Shape 对象所代表的确切类型。这也是一种新的封装思想。 Shape 类封装了形状的确切类型。 Adapter 应用得最多的目的就是使我们能够有机会使用多态。在随后的章节中你就会发现其他很多模式都要求用到多态。 <o:p> </o:p>

<o:p> </o:p>

<o:p> </o:p>

<o:p> </o:p>

我们通常都会遇到和前面例子相似的情况,但是有时,被适配的对象并不一定能完全满足我们的需要。 <o:p> </o:p>

<o:p> </o:p>

在这样的情况下,我们仍然可以用到 Adapter 模式,但似乎不如先前的那么完美了。在这样的一个情况下: <o:p> </o:p>

l          在既存在的类中已经实现的功能可以被我们适配。 <o:p> </o:p>

l          那些我们需要但又不在既存在类中的方法,我们可以在适配器类中去实现。 <o:p> </o:p>

<o:p> </o:p>

虽然这样并不如先前例子中的那么好,但是,至少,我们只需要再去实现一部分功能就可以了。 <o:p> </o:p>

<o:p> </o:p>

Adapter 模式使我在设计过程中不再为那些已经存在的接口所烦恼。如果一个类可以完成我的工作,那么至少从理论上讲,我完全可以通过 Adapter 模式来得到适当的接口。 <o:p> </o:p>

<o:p> </o:p>

当你学习了更多的模式以后,你会越发发现 Adapter 的重要性。很多模式都要求某些类都继承自同一个类。如果有既存在的类, Adapter 模式就可以用来将该类适配给恰当的抽象类 ( 就象 Circle XXCircle 适配给 Shape 那样 ) <o:p> </o:p>

<o:p> </o:p>

通过我们讲到的这么些类,总有人会认为好像 Adapter 模式和 Facade 模式是一回事。他们都有既存在的类,都没有恰当的接口,我们都创建了包含恰当接口的新对象。 <o:p> </o:p>

<o:p>  </o:p>

7-6 客户对象 使用 既存在 但拥有不恰当接口 的对象 <o:p> </o:p>

我们常听到 包装 对对象进行包装 的说法。现在很流行对包装遗留系统 (wrapping legacy system) 和使对象变的简单可用的思考。 <o:p> </o:p>

<o:p> </o:p>

从这个高度来说, Facade Adapter 的确有点相似,他们都是进行 包装 。但他们是不同类型的包装方式。你应该明白这其中的小小不同。找到并理解到这其中的不同将给我们一个对模式的性质的新视角,在讨论一个设计或者给设计写文档以使别人知道对象的确切用途的时候,这些都非常有用。让我们来看看 Facade Adapter 有何不同。 <o:p> </o:p>

Facade 模式和 Adapter 模式的比较 <o:p> </o:p>

Facade<o:p></o:p>

Adapter<o:p></o:p>

是否有既存在的类? <o:p> </o:p>

<o:p> </o:p>

<o:p> </o:p>

是否有必须实现的接口? <o:p> </o:p>

分享到:
评论

相关推荐

    适配器模式t31

    适配器模式是一种软件设计模式,它允许两个不兼容的接口之间进行通信。在软件工程中,这种模式常被用来解决新旧系统对接、不同组件间的兼容性问题,或者为了复用已有代码而进行的接口转换。适配器模式的核心思想是...

    适配器模式1

    适配器模式是一种软件设计模式,它允许两个不兼容的接口之间进行通信。在C++中,适配器模式可以通过类继承或者对象组合的方式实现。类适配器模式利用多重继承,让适配器类同时继承目标接口和适配者类,而对象适配器...

    适配器模式Demo

    适配器模式是一种软件设计模式,它允许两个不兼容的接口之间进行通信。在实际的软件开发中,我们经常会遇到需要将现有类的功能融入到新系统中,但这些类的接口与新系统的要求不匹配的情况。适配器模式就提供了一个...

    适配器模式笔记1

    【适配器模式笔记1】 适配器设计模式是一种结构型设计模式,它允许不同的类协同工作,即使它们的接口不兼容。适配器模式的关键在于将一个类的接口转换成客户端期望的另一种接口,从而使得原本由于接口不兼容而不能...

    13适配器模式1

    适配器模式是一种软件设计模式,它允许两个不兼容的接口之间进行通信。在软件开发过程中,有时我们可能需要使用一些已经存在的类或组件,但它们的接口与我们的系统不匹配,这时候适配器模式就能派上用场。适配器模式...

    php适配器模式介绍

    适配器模式是一种软件设计模式,它允许两个不兼容的接口之间进行通信。在PHP开发中,当需要复用已有的类或者库,但它们的接口与当前项目的需求不匹配时,适配器模式就显得尤为有用。适配器模式主要有对象适配器和类...

    c# 适配器模式

    适配器模式是一种设计模式,它在软件工程中扮演着重要的角色,特别是在处理不同系统、组件或类之间的接口不兼容性时。C#中的适配器模式同样遵循这一原理,帮助开发者解决接口不匹配的问题,使得原本无法协作的类能够...

    java中适配器模式案例

    适配器模式是一种设计模式,它允许不兼容的类或接口之间进行通信,通过创建一个适配器类作为中间桥梁,使得原本不匹配的接口能够协同工作。在Java中,适配器模式广泛应用于系统集成、旧代码复用以及第三方库的兼容性...

    Java适配器模式定义与用法示例

    Java适配器模式定义与用法示例 Java适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户想要的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。该模式主要包括三个角色:目标、...

    详解Java适配器模式

    Java 适配器模式详解 Java 适配器模式是一种结构型设计模式,主要用来解决不同接口之间的不兼容问题。在软件开发中,经常会遇到两个不同的接口之间需要进行交互的情况,这时就需要使用适配器模式来解决问题。 ...

    适配器模式、适配器模式

    适配器模式

    CCNU的设计模式-适配器模式

    适配器模式有两种实现方式,一种是类适配器模式,一种是对象适配器模式。类适配器模式通过多重继承的方式来实现适配器,而对象适配器模式则通过组合的方式实现。 类适配器模式中,适配器类继承自Adaptee类,并实现...

    类适配器, 对象适配器模式,缺省适配器模式1

    适配器模式分为类适配器模式和对象适配器模式,还有缺省适配器模式。 1. **类适配器模式**: 在类适配器模式中,适配器类通过继承被适配类(Adaptee)并实现目标接口(Target)。这种方式下,适配器和被适配类之间...

    PHP设计模式之适配器模式原理与用法分析

    适配器模式有两种:类适配器模式和对象适配器模式。其中类适配器模式使用继承方式,而对象适配器模式使用组合方式。由于类适配器模式包含双重继承,而PHP并不支持双重继承,所以一般都采取结合继承和实现的方式来...

    c#设计模式 适配器模式详细介绍

    适配器模式是一种在软件设计中广泛使用的结构型模式,其主要目的是解决不同接口或类之间接口不兼容的问题,使得原本不能一起工作的类能够协同工作。适配器模式通过创建一个适配器类,将旧的或不兼容的接口转换为客户...

    8. 适配器模式1

    适配器模式是一种设计模式,它的主要目的是解决不兼容接口之间的通信问题,使得原本由于接口不匹配而无法协同工作的组件可以有效地协作。在软件工程中,适配器模式扮演着“桥梁”的角色,将旧的或者不同的接口转换成...

Global site tag (gtag.js) - Google Analytics