36
The Adaptor Pattern 结结结结结结结结结结结 ()

The Adaptor Pattern (结构模式之适配器模式)

  • Upload
    keagan

  • View
    198

  • Download
    15

Embed Size (px)

DESCRIPTION

The Adaptor Pattern (结构模式之适配器模式). 现实生活中的适配器. 现实生活中的适配器. 面向对象软件系统的适配问题. 假设我们已经有一个软件系统,原来使用了一个第三方类库 A 。现在有一个新的第三方类库 B ,其功能等各方面都更加强大。我们希望用 B 来替换 A ,以改善我们的系统。但是 B 的接口与 A 不一样。那则么办呢?. B. System. A. New System. 办法之一. B. New System. 办法之一. B. System. A. Adapter. 办法之二. B. System. Adapter. - PowerPoint PPT Presentation

Citation preview

The Adaptor Pattern(结构模式之适配器模式)

现实生活中的适配器

现实生活中的适配器

面向对象软件系统的适配问题 假设我们已经有一个软件系统,原来使用了一个第三方类库 A 。现在有一个新的第三方类库 B ,其功能等各方面都更加强大。我们希望用 B 来替换 A ,以改善我们的系统。但是 B 的接口与 A 不一样。那则么办呢?

办法之一ASystem BNew

System

办法之一

BNew System

B

Adapter

办法之二

ASystem

BAdapter

第二种方案的优点

System

不需要修改代码 不需要修改代码新代码

办法之三B’System B

下面我们来看一个实际的例子 简化的鸭子接口和类public interface

Duck {public void quack();public void fly();

} 鸭子接口 Duck ,定义了鸭子具有“鸣叫”和“飞行”方法

MallardDuck 类public class

MallardDuck implements Duck {public void quack() {System.out.println(" 嘎嘎嘎 ...");}public void fly() {System.out.println(" 我在飞哦! ");}

}

MallardDuck 类简单地实现了Duck 接口。

现在有一种新家伙public interface

Turkey {public void gobble();public void fly();

}

WildTurkey

public class WildTurkey implements Turkey {public void gobble() {System.out.println(" 咕咕咕 ...");}public void fly() {System.out.println(" 我在飞,不过飞不远。 ");}

}

我们原来有一个程序使用鸭子对象,现在想让它使用火鸡对象,但是火鸡与鸭子的接口不同,不能直接使用。 写一个火鸡适配器,让火鸡看起来像鸭子

火鸡适配器public class TurkeyAdapter implements

Duck {Turkey turkey;public TurkeyAdapter(Turkey turkey) {this.turkey = turkey;}public void quack() {turkey.gobble();}public void fly() {for(int i=0; i < 5; i++) {turkey.fly();}}

}

火鸡适配器包装了一个火鸡对象,同时实现了鸭子接口。这样就可以像使用鸭子一样使用火鸡了。

使用适配器public class DuckTestDrive {

public static void main(String[] args) {MallardDuck duck = new MallardDuck();WildTurkey turkey = new WildTurkey();Duck turkeyAdapter = new TurkeyAdapter(turkey);System.out.println(" 火鸡说 ...");turkey.gobble();turkey.fly();System.out.println("\n 鸭子说 ...");testDuck(duck);System.out.println("\n 火鸡适配器说 ...");testDuck(turkeyAdapter); }static void testDuck(Duck duck) {duck.quack();duck.fly();}

} 需要使用鸭子对象

在需要鸭子对象的地方使用了火鸡适配器对象, 火鸡适配器对象包装了一个火鸡对象,所以实际使用的是火鸡对象。

适配器模式详解

被适配者火鸡接口

客户要使用鸭子对象的程序

适配器把火鸡装扮成鸭子

目标接口 : 鸭子接口

两者无耦合彼此不必知道对方的存在两者无耦合彼此不必知道对方的存在

试试看 现在,如果希望把鸭子包装成火鸡该怎么做? 写出你的代码 DuckAdapter

DuckAdapter 参考答案import java.util.Random;public class DuckAdapter implements Turkey {

Duck duck;Random rand;public DuckAdapter(Duck duck) {this.duck = duck;rand = new Random();}public void gobble() {duck.quack();}public void fly() {if (rand.nextInt(5) == 0) { duck.fly();}}

}

Adapter 模式 定义

将一个类的接口转换成客户端所期望的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。 别名

包装器 Wrapper

Adapter 模式 动机

有时,为复用而设计的工具箱类不能够被复用的原因仅仅是因为它的接口与专业应用领域所需要的接口不匹配。 我们可以改变工具箱类使它兼容专业领域中的类的接口,但前提是必须有这个工具箱的源代码。然而即使我们得到了这些源代码,修改工具箱也是没有什么意义的;因为不应该仅仅为了实现一个应用,工具箱就不得不采用一些与特定领域相关的接口。

Adapter 模式 动机(续)

我们可以不用上面的方法,而定义一个适配器类,由它来适配工具箱的接口和专业应用的接口。我们可以用两种方法做这件事: 1) 继承专业应用类的接口和工具箱类的实现。这种方法对应 A d a p t e r 模式的类版本(多继承) 2) 将一个工具箱类的实例作为适配器类的组成部分,并且使用工具箱的接口实现适配器类。这种方法对应 A d a p t e r 模式的对象版本。

Adapter 模式 适用性 以下情况使用 A d a p t e r 模式

你想使用一个已经存在的类,而它的接口不符合你的需求。 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。 (仅适用于对象 A d a p t e r )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

Adapter 模式 结构(类版本)

Adapter 模式 结构(对象版本)

基于类的 Adapter 模式 基于类的 Adapter 模式的一般结构如下:

Adaptee 类为 Adapter 的父类, Adaptee 类为适配源,适配目标(接口)也是 Adapter 的父类;基于类的 Adapter 模式比较适合应用于Adapter 想修改 Adaptee 的部分方法的情况。

基于对象的 Adapter 模式 基于对象的 Adapter 模式的一般结构如下:

Adaptee 类对象为 Adapter 所依赖,适配目标(接口)是 Adapter 的父类; 基于对象的 Adapter 模式比较适合应用于

Adapter 想为 Adaptee添加新的方法的情况。但在 Adaptee 类的方法与 Adapter 类的方法不同名而实现相同功能的情况下,我们一般也使用基于对象的 Adapter 模式,

Adapter 模式 参与者

Ta r g e t 定义 C l i e n t 使用的与特定领域相关的接口。

C l i e n t 与符合 Ta rg e t 接口的对象协同。

A d a p t e e 定义一个已经存在的接口,这个接口需要适配。

A d a p t e r 对 A d a p t e e 的接口与 Ta rg e t 接口进行适配

Adapter 模式 协作

Client 在 A d a p t e r 实例上调用一些操作(请求)。接着适配器调用 A d a p t e e的操作实现这个请求。

Adapter 模式 效果(类适配器和对象适配器有不同的权衡)

类适配器 用一个具体的 A d a p t e r 类对 A d a p t e e 和 Ta rg

e t 进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类 A d a p t e r 将不能胜任工作。 使得 A d a p t e r 可以重定义 A d a p t e e 的部分行为,因为 A d a p t e r 是 A d a p t e e 的一个子类。 仅仅引入了一个对象,并不需要额外的指针以间接得到 a

d a p t e e 。

Adapter 模式 效果(类适配器和对象适配器有不同的权衡)

对象适配器则 允许一个 A d a p t e r 与多个 A d a p t e e— 即 A d a

p t e e 本身以及它的所有子类(如果有子类的话)同时工作。 A d a p t e r 也可以一次给所有的 A d a p t e e添加功能。 使得重定义 A d a p t e e 的行为比较困难。这就需要生

成 A d a p t e e 的子类并且使得 A d a p t e r引用这个子类而不是引用 A d a p t e e 本身。

???? 前面的实例是 A d a p t e r 模式的那种版本?

分析一下例子,确定各个类的角色public class WebFormDesigner extends JFrame {

void jbInit() throws Exception {

text.addActionListener(new

WebFormDesigner_text_actionAdapter(this)); }

void text_actionPerformed(ActionEvent e){TextDialog createText = new TextDialog();createText.setVisible(true);createText.setTitle("TEXT");createText.setBounds(100, 100, 500, 300);createText.setModal(true);createText.show();}

}

分析一下例子,确定各个类的角色 --- 续class WebFormDesigner_text_actionAdapter

implements java.awt.event.ActionListener {

WebFormDesigner adaptee;

WebFormDesigner_text_actionAdapter(WebFormDesigner adaptee) {

this.adaptee = adaptee;}

public void actionPerformed(ActionEvent e) {

adaptee.text_actionPerformed(e);}

}

分析WebFormDesigner 在本例中是 Adaptee,

WebFormDesigner_text_actionAdapter 在本例中是 Adapter, java.awt.event.ActionListener 在本例中是适配目标。适配目标为一接口,代码如下:

public interface ActionListener extends EventListener {/*** Invoked when an action occurs.*/public void actionPerformed(ActionEvent e);}适配目标中只有一个方法: actionPerformed ()。由于

WebFormDesigner_text_actionAdapter 实现了java.awt.event.ActionListener ,所以要求实现 actionPerformed ()方法。WebFormDesigner_text_actionAdapter 实现 actionPerformed ()方法是引用了 WebFormDesigner 这个 adaptee 来完成的。从代码中可以看出, WebFormDesigner 是 WebFormDesigner_text_actionAdapter 的方法的参数,所以 WebFormDesigner_text_actionAdapter依赖WebFormDesigner 。

Adaptee 类( WebFormDesigner )的方法( text_actionPerformed )与Adapter 类( WebFormDesigner_text_actionAdapte )的方法( actionPerformed )不同名而实现相同功能,这就是我们上面分析的基于对象的 Adapter 模式。

作业 (下周上机完成) 请设计一个 Kittie 的具体类,并实现

miao(),run(),sleep(),catchRat() 等方法。 再设计一个 Puppie 接口,要求有 wao(),

fetchBall(), run(), sleep() 等方法。 设计一个适配器,将一只猫 kittie伪装成小狗

puppie 。 编写一个测试主类,测试你的程序。 实验后一周内将实验报告发至

[email protected]