Adapter模式

Adapter模式

如果想让额定工作电压是直流12伏特的笔记本电脑在交流100伏特的AC电压下工作,应该怎么做呢?通常,我们会使用AC适配器,将家庭用的交流100伏特电压转换成我们所需要的直流12伏特电压.这就是适配器的工作,它位于世纪情况与需求之间,填补两者之间的差异.适配器的英文是Adapter,意思是"使…相互适合的东西".前面说的AC适配器的工作就是让工作于直流12伏特环境的笔记本电脑适合于交流100伏特的环境.

适配器角色
在程序世界中,经常会存在现有的程序无法直接使用,需要做适当的变换之后才能使用的情况.这种用于填补"现有的程序"和"所需的程序"之间的差异的设计模式就是Adapter模式.
Adapter模式也被称为Wrapper模式.Wrapper有"包装器"的意思,就像用精美的包装纸将普通商品包装成礼物那样,替我们把某样东西包起来,使其能够用于其他用途的东西就被称为"包装器"或是"适配器"
Adapter模式有以下两种.
一. 类适配器模式(使用继承的适配器)
二.对象适配器模式(使用委托的适配器)

示例程序(1)(使用继承的适配器)
示例程序是一段将输入的字符串显示为(Hello)或是Hello的简单程序.
目前在Banner类(Banner有广告横幅的意思)中,有将字符串用括号括起来的showWithParen方法,和将字符串用号括起来的showWithAster方法.我们假设这个Banner类是类似前文中的"交流100伏特电压"的"实际情况"
假设Print接口中声明了两种方法,即弱化字符串显示(加括号)的printWeak(weak有弱化的意思)方法,和强调字符串现实的(加
号)的printStrong(strong有强化的意思)方法.我们假设这个接口是类似于前文的"直流12伏特电压"的"需求"
现在要做的事情是使用Banner类编写一个实现了Print接口的类,也就是说要做一个将"交流100伏特电压"转换成"直流12伏特电压"的适配器
扮演适配器角色的是printBanner类.该类继承了Banner类并实现了"需求"----Print接口.PrintBanner类使用showWithParen方法实现了printWeak,使用showWithAster方法实现了printStrong.这样,PrintBanner类就具有适配器的功能了.电源的比喻和示例程序的对应关系如下:
Adapter模式_第1张图片

使用了"类适配器模式"德 示例程序的类图(使用继承)
Adapter模式_第2张图片

Banner类
假设Banner类是现在的实际情况

public class Banner {
    private String string;
    public Banner(String string){
        this.string = string;
    }
    public void showWithParen(){
        System.out.println("("+string+")");
    }
    public void showWithAster(){
        System.out.println("*"+string+"*");
    }
}

Print接口

public interface Print {
    public abstract void printWeak();
    public abstract void printStrong();
}

PrintBanner类
PrintBanner类扮演适配器的角色.它继承(extends)了Banner类,继承了showWithParen方法和showWithAster方法.同时,它又实现(implements)了Print接口,实现了printWeak方法和printStrong方法.

printBanner类(PrintBanner.java)

public class PrintBanner extends Banner implements Print {
    public PrintBanner(String string){
        super(string);
    }
    public void printWeak(){
        showWithParen();
    }
    public void printStrong(){
        showWithAster();
    }
}

Main类
Main类的作用是通过扮演适配器角色的PrintBanner类来弱化(待括号)或是强化Hello(待*号)字符串的显示

Main类(Main.java)

public class Main {

    public static void main(String[] args) {
        Print p = new PrintBanner("Hello");
        p.printWeak();
        p.printStrong();
    }
}

运行结果

(Hello)
*Hello*

Process finished with exit code 0

我们是使用Print接口(即电泳printWeak方法和printStrong方法)来进行编程的.对Main类的代码而言,Banner类,showWithParen方法和showWithAster方法被完全隐藏起来了.这就好像笔记本电脑只要在直流12伏特电压下就能正常工作,但它并不知道这12伏特的电压是由适配器将100伏特交流电压转换而成的.
Main类并不知道PrintBanner类是如何实现的,这样就可以在不用对Main类进行修改的情况下改变PrintBanner类的具体实现.

示例程序2(使用委托的示例程序)
之前的示例程序展示了适配器模式.下面我们再来看看对象适配器模式.在之前的示例程序中,我们使用"继承"实现适配,而这次我们要使用"委托"来实现适配.
Java语言中,委托就是指将某个方法中的实际处理交给其他实例的方法.
Main类和Banner类的示例程序(1)中的内容完全相同,不过这里我们假设Print不是接口而是类.
也就是说,我们打算利用Banner类实现一个类,该类的方法和Print类的方法相同.由于在Java中无法同时继承两个类(只能是单一继承),因此我们无法将PrintBanner类分别定义为Print类和Banner类的子类
PrintBanner类的banner字段中保存了Banner类的实例.该实例是在PrintBanner类的构造函数中生成的.然后,printWeak方法和printStrong方法会通过banner字段调用Banner类的showWithParen和showWithAster方法.
与之前的实例代码中调用了从父类中继承的showWithParen方法和showWithAster方法不同,这次我们通过字段来调用这两个方法.
这样就形成了一种委托关系一种委托关系.当PrintBanner类的printWeak被调用的时候,并不是PrintBanner类自己进行处理,而是将处理交给了其他实例(Banner类的实例)的showWithParen方法.
Adapter模式_第3张图片
Print类(Print.java)

public abstract class Print {
    public abstract void printWeak();
    public abstract void printStrong();
}

Adapter模式中的登场角色
在Adapter模式中有以下登场角色.
Target(对象)
该角色负责定义所需的方法.以本章开头的例子来说.即让笔记本电脑正常工作所需的直流12伏特电源.在示例程序中,由Print接口(使用继承时)和Print类(使用委托时)扮演次角色.
Client(请求者)
该角色负责使用Target角色所定义的方法进行具体处理.以本章开头的例子来说,即直流12伏特电源所驱动的笔记本电脑.在示例程序中,由Main类扮演次角色
Adaptee(被适配)
注意不是Adapt-er(适配)角色,而是Adapt-ee(被适配)角色.Adaptee是一个持有既定方法的角色.即交流100伏特电源.在示例程序中,由Banner扮演此角色
如果Adaptee角色中的方法与Target角色的方法相同(也就是说家庭使用的电压就是 12伏特直流电压).就不需要接下来的Adapter角色了
Adapter(适配)
Adapter模式的主人公.使用Adaptee角色的方法来满足Target角色的需求,这是Adapter模式的目的,也是Adapter角色的作用.Adapter角色就是将交流100伏特电压转换为直流12伏特电压的适配器.在示例程序中,由PrintBanner类扮演这个角色.
在类适配器模式中,Adapter角色通过委托来使用Adaptee角色,而在对象适
配器模式中,Adapter角色通过委托来使用Adapter角色
类适配器模式的类图(使用继承)
Adapter模式_第4张图片

对象适配器模式的类图(使用委托)

Adapter模式_第5张图片
什么时候使用Adapter模式
很多时候,我们并非从零开始编程,经常会用到现有的类.特别是当现有的类已经被充分测试过了,bug很少,而且已经被用于其他软件之中时,我们更愿意将这些作为组件重复利用
Adapter模式会对现有的类进行适配,生成新的类.通过该模式可以很方便地创建我们需要的方法群.当出现Bug时,由于我们很明确地知道Bug不在现有的类(Adaptee角色)中,所以只需要调查扮演Adapter角色的类即可.这样一来,代码问题的排查就会变得非常简单.
如果没有现成的代码
让现有的类适配新的接口(API)时,使用Adapter模式似乎是理所当然的.不过实际上,我们在让现有的类适配心的接口时,常常会有"只要将这里稍微修改下就可以了" 的想法,一不留神就会修改现有的代码.但需要注意的是,如果要对已经测试完毕的现有代码进行修改,就必须在修改后重新进行测试.
使用Adapter模式可以在完全不改变现有代码的前提下使现有代码适配于新的接口(API)此外,在Adapter模式中,并非一定需要现成的代码.只要知道现有类的功能,就可以编写出新的类
版本升级与兼容性
软件的生命周期总是伴随着版本的升级,而在版本升级的时候经常会出现"与旧版本的兼容性"问题.如果能够完全抛弃旧版本,那么软件的维护工作将会轻松得多,但是现实中往往无法这样做.这时,可以使用Adapter模式使用新版本的类来实现旧版本的类中的方法.
功能完全不同的类
当然,当Adapter角色和Target角色的功能完全不同时,Adapter模式是无法使用的.就如同我们无法用交流100伏特电压让自来水管出水一样

你可能感兴趣的:(设计模式,Adapter模式)