Adapter (设配器)模式

2.1 Adapter 模式

  如果想让额定工作电压是直流12伏特的笔记本电脑在交流 100 伏特的 AC 电源下工作,通常我们会使用 AC 适配器,将家庭用的 100 伏特电压转换成我们需要的直流 12 伏特电压。这就是适配器的工作,它位于实际情况与需求之间,填补两者之间的差异。适配器的英文是 Adapter,“使…互相适合的东西”。
  在程序世界中,经常会存在现有的程序无法直接使用,需要做适当的变换之后才能使用的情况。这种用于填补“现有的程序”和“所需的程序”之间的差异的设计模式就是 Adapter 模式
  Adapter 模式也被称为 Wrapper 模式。Wrapper 有“包装器”的意思,就像用精美的包装纸将普通商品包装成礼物那样,把某样东西包起来,使其能够用于其他用途的东西就被称为“包装器”或是“适配器”。
  Adapter 模式有以下两种:

  • 类适配器模式(使用继承的适配器)
  • 对象适配器模式(使用委托的适配器)
2.2 示例程序(1)(使用继承的适配器)

  首先,一段使用继承的适配器的示例程序。将输入的字符串显示为(Hello)或是 Hello 的简单程序。
  在 Banner 类中,有将字符串用括号括起来的 showWithParen 方法,和将字符串用 * 号括起来的 showWithAster 方法。
  假设 Print 接口中声明了两种方法,即弱化字符串显示的 printWeak 方法,和强调字符串显示的 printStrong 方法。
  现在要做的事情是使用 Bannner 类编写一个实现了 Print 接口的类。其中扮演适配器角色的是 PrintBanner 类。该类继承了 Banner 类并实现了“需求” - print 接口。PrintBanner 类使用 showWithParen 方法实现了 printWeak,使用 showWithAster 方法实现了 printStrong。这样,PrintBanner 类就具有适配器的功能了。
使用了“类适配器模式”的示例程序类图(继承)

使用了“类适配器模式”的示例程序类图(继承)
|| Banner 类
/**
*
* describe 待适配的类
* @author xmc
*/
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 接口

  需求接口

/**
*
* describe 需要适配的接口
* @author xmc
*/
public interface Print {
   /**
    *
    * describe 弱化字符串显示
    * @author xmc
    */
   void printWeak();

   /**
    *
    * describe 强调字符串显示
    * @author xmc
    */
   void printStrong();
}
|| PrintBanner 类

  PrinterBannner 类扮演适配器的角色。它继承了Banner 类,同时又实现了 Print 接口。

/**
*
* describe 适配器角色-继承了 Banner 类,实现了 Print 接口
* @author xmc
*/
public class PrintBanner extends Banner implements Print {

   public PrintBanner(String str) {
      super(str);
   }

   /**
    *
    * describe 弱化字符串显示
    * @author xmc
    */
   @Override
   public void printWeak() {
      showWithParen();
   }

   /**
    *
    * describe 强调字符串显示
    * @author xmc
    */
   @Override
   public void printStrong() {
      showWithAster();
   }
}
|| Main 类

  运行主类,通过扮演适配器角色的 PrintBanner 类来弱化或强化字符串的显示。

public class Main {
   public static void main(String[] args) {
      // 向上转型为 Print 接口
      Print print = new PrintBanner("Hello");
      print.printWeak();
      print.printStrong();
   }
}

结果

(Hello)
*Hello*

  这里我们将 PrintBanner 类的实例保存到了 Print 类型的变量中。在 Main 类中我们使用 Print 接口(printWeak、printStrong方法)来进行编程的。对 Main 类的代码而言,Banner 类 及其 showWithParen 方法和 showWithAster 方法被完全隐藏起来了。
  Main 类并不知道 PrintBanner 类是如何实现的,这样就可以在不用对 Main 类进行修改的情况下改变 PrintBanner 类的具体实现。

2.3 示例程序(2)(使用委托的示例程序)

  在之前的示例程序中,我们使用了 “继承” 实现适配,现在我们使用 “委托” 来实现适配(在 Java 语言中,委托是指将某个方法中的实际处理交给其他实例的方法)。
  情景与示例(1)完全相同,不过这里我们假设 Print 不是接口而是抽象类。
  也就是说,打算利用 Banner 类实现一个类,该类的方法和 Print 类的方法相同。但在 Java 中无法同时继承两个类(单一继承),因此无法将 PrintBanner 类分别定义为 Print 类和 Banner 类的子类。
  PrintBannner 类的 banner 字段保存了 Banner 类的实例。该实例在 PrintBanner 类的构造方法中生成。然后,printWeak 和 printStrong 方法会通过 banner 字段调用 Banner 类的 showWithParen 和 showWithAster 方法。即通过字段来调用这两个方法
  这就形成了一种委托关系。当 PrintBanner 类的 printWeak 被调用的时候,并不是 PrintBanner 类自己进行处理,而是将处理交给了其他实例的方法。
使用了“对象适配器模式”的示例程序类图(委托)

使用了“对象适配器模式”的示例程序类图(委托)
|| Print 类
/**
*
* describe 抽象类接口
* @author xmc
*/
public abstract class Print {

   abstract void printWeak();

   abstract void printStrong();
}
|| PrintBanner 类
/**
*
* describe 使用委托的适配器来实现适配器模式
* @author xmc
*/
public class PrintBannerEntrust extends Print {
   private Banner banner;

   public PrintBannerEntrust(Banner banner) {
      this.banner = banner;
   }

   @Override
   void printWeak() {
      banner.showWithParen();
   }

   @Override
   void printStrong() {
      banner.showWithAster();
   }
}
2.4 Adapter 模式模式中的登场角色
  • Target (对象)
      该角色负责定义所需的方法。在示例程序中,由 Print 接口(继承时)和 Print 类(委托时)扮演此角色。
  • Client (请求者)
      该角色负责使用 Target 角色所定义的方法进行具体处理。即笔记本所需直流 12 伏特电源锁驱动的笔记本电脑。在示例程序中,由 Main 类扮演此角色。
  • Adaptee (被适配)
      Adaptee(被适配)角色,是一个持有既定方法的角色。以笔记本电脑为例,则是交流 100 伏特电压。在示例程序中,由 Banner 类扮演此角色。如果 Adaptee 角色中的方法与 Target 角色的方法相同,就不需要接下来的 Adapter 角色了。
  • Adapter (适配)
      Adapter 模式主人公。使用 Adaptee 角色的方法来满足 Target 角色的需求,这是 Adapter 模式的目的,也是 Adapter 角色的作用。以笔记本为例, Adapter 角色就是将交流 100 伏特电压转换为直流 12 伏特电压的适配器。在示例程序中,由 PrintBanner 类扮演这个角色。
      在类适配器中,Adapter 角色通过继承来使用 Adaptee 角色,而在对象适配器模式中, Adapter 角色通过委托来使用 Adaptee 角色。
    类适配器模式的类图(继承)
类适配器模式的类图(继承)

在这里插入图片描述

对象适配模式的类图(委托)
2.5 拓展思路的要点
|| 什么时候使用 Adapter 模式

  “如果某个方法就是我们所需要的方法,那么直接在程序中使用不就可以了吗?为什么还要使用 Adapter 模式?”,究竟应当在上面时候使用 Adapter 模式呢?
  很多时候,经常会用到现有的类。特别是当现有的类已经被充分测试过了,Bug 很少,而且已经被用于其他软件之中时,我们愿意将这些类作为组件重复利用。
  Adapter 模式会对现有的类进行适配,生成新的类。通过该模式可以很方便的创建我们需要的方法群。当出现 Bug 时,我们明确知道问题不在现有的(Adaptee)类中,只需要调查扮演 Adapter 角色的类即可。

|| 如果没有现有的代码

  让现有的类适配新的接口时,使用 Adapter 模式似乎是理所当然的。不过实际上,我们在让现有的类适配新的接口时,常常会有 “只要将这里稍微修改下就可以了” 的想法,一不留神就会修改现有的代码。但需要注意的是,如果要对已经测试完毕的现有代码进行修改,就必须在修改后重新进行测试
  使用 Adapter 模式可以在完全不改变现有代码的前提下使现有代码适配于新的接口。此外,在 Adapter 模式中,并非一定需要现成的代码。只要知道现有类的功能,就可以编写出新的类。

|| 功能完全不同的类

  当 Adaptee 角色和 Target 角色的功能完全不同时, Adapter 模式是无法使用的。如果我们无法用交流 100 伏特电压让自来水管出水一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值