「手写设计模式」适配器模式

242 阅读3分钟

理解名词

适配器模式(Adapter Design Pattern)是一种结构型设计模式。

关于适配器,可以用Type-C扩展坞做例子,对于只支持Type-C接口的MAC来说,如果想使用USB接口的键盘,HDMI接口的视频输出线,网线接口,SD卡读卡槽,需要使用Type-C转USB,Type-C转HDMI,Type-C转RJ-45等等适配器。这里体现了适配器模式的一个作用,统一多个类的接口设计,做统一的好处就是使我们开发系统时不用考虑外部系统的差异性,留出接口让外部系统匹配我们即可。

再做个比方,旧计算机的视频输出口是VGA接口,但是现在的新显示器都只支持HDMI接口了,那旧计算机想使用新显示器,就需要VGA转HDMI适配器。这里引出了适配器模式的第二个作用,兼容老版本接口

作用

  • 封装有缺陷的接口设计。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。
  • 统一多个类的接口设计。某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。
  • 替换依赖的外部系统。当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。
  • 兼容老版本接口。在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。
  • 适配不同格式的数据。比如,把从不同系统拉取的不同格式的数据,统一为相同的格式,以方便存储和使用。

适用场景

接口不兼容

代码示例

ITarget 表示要转化成的接口定义。Adaptee 是一组不兼容 ITarget 接口定义的接口,Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口。

第一种,基于继承的类适配器

public interface ITarget {
  void f1();
  void f2();
  void fc();
}

public class Adaptee {
  public void fa() { //... }
  public void fb() { //... }
  public void fc() { //... }
}

public class Adaptor extends Adaptee implements ITarget {
  public void f1() {
    super.fa();
  }
  
  public void f2() {
    //...重新实现f2()...
  }
  
  // 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点
}

第二种,基于组合的对象适配器

public interface ITarget {
  void f1();
  void f2();
  void fc();
}

public class Adaptee {
  public void fa() { //... }
  public void fb() { //... }
  public void fc() { //... }
}

public class Adaptor implements ITarget {
  private Adaptee adaptee;
  
  public Adaptor(Adaptee adaptee) {
    this.adaptee = adaptee;
  }
  
  public void f1() {
    adaptee.fa(); //委托给Adaptee
  }
  
  public void f2() {
    //...重新实现f2()...
  }
  
  public void fc() {
    adaptee.fc();
  }
}

使用哪一种适配方式有两个判断标准,一个是 Adaptee 接口的个数,另一个是 Adaptee 和 ITarget 的契合程度。

  • 如果 Adaptee 接口并不多,那两种实现方式都可以。
  • 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都相同,那我们推荐使用类适配器,因为 Adaptor 复用父类 Adaptee 的接口,比起对象适配器的实现方式,Adaptor 的代码量要少一些。
  • 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都不相同,那我们推荐使用对象适配器,因为组合结构相对于继承更加灵活。

业界经典实现

  • Sl4j日志框架