木灵鱼儿

木灵鱼儿

阅读:332

最后更新:2022/06/05/ 22:29:46

结构型模式:适配器模式

简介

适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。

举个简单的例子:

我们的插排提供的是220v的电压,而我们的手机能支持的是5v电压,此时它们之间是无法协同工作的,所以我们出现了电源适配器(手机充电器),电源适配器将220v转为了5v电压供手机使用。

而适配器模式有三类:类适配器模式、对象适配器模式、接口适配器模式。

类适配器模式

适配器模式中有三种主要角色:被适配者(src),适配器(adapter),目标的实现接口(dst);

目标的实现接口,以上面电源适配器的例子来说,手机要求的是一个5v的电压,这个要求就可以抽象成一个接口,让手机依赖这个接口,适配器也依赖于这个接口,依赖倒置原则。

类适配器(adapter)会继承被适配者,依赖目标的实现接口,实现5v的电压输出。

客户端就可以利用适配器给手机充电了。

看个类图:

代码实现:

//被适配者
class Voltage220V {
  output220(): number {
    return 220;
  }
}

//目标的实现接口
interface IVoltage5V {
  output5(): number;
}

//适配器
class Voltage5V extends Voltage220V implements IVoltage5V {
  output5() {
    const power = this.output220();
    const chagePower = power / 44;
    return chagePower;
  }
}

//手机
class Phone {
  //充电方法
  charging(voltage: IVoltage5V) {
    const power = voltage.output5();

    if (power === 5) {
      console.log("可以充电");
    } else {
      console.log("电压异常,无法充电");
    }
  }
}

//客户端调用
class Client {
  constructor() {
    //手机实例
    const phone = new Phone();
    //给手机充电
    phone.charging(new Voltage5V());
  }
}

new Client();  //可以充电

还是挺简单的。

注意事项

  1. 由于适配器继承的被适配的类,导致适配器会暴露一些继承过来的接口方法,增加了使用成本
  2. 虽然我们可以根据需求,通过重写接口增加灵活性,但是这就不满足里氏替换原则了。

对象适配器

我们发现类适配器,他会有继承关系,因为继承了导致适配器会有很多它本身不需要的接口或者方法,这显然不是很好,我们可以通过对象适配器的方式,通过合成复用原则,将被适配者聚合到适配器中去,从而斩断继承关系。

此时,适配器只需要提供一个接口接受被适配者,再提供一个客户端需要的接口就可以了,没有冗余的方法了。

我们来看个类图:

客户端Clinet依赖220v和适配器,其实就是客户端将220v传递给适配器。

我们来看下代码的实现:

//被适配者
class Voltage220V {
  output220(): number {
    return 220;
  }
}

//目标的实现接口
interface IVoltage5V {
  output5(): number;
}

//适配器
class Voltage5V implements IVoltage5V {
  voltage220V: Voltage220V;

  constructor(voltage220V: Voltage220V) {
    this.voltage220V = voltage220V;
  }

  output5() {
    const power = this.voltage220V.output220();
    const chagePower = power / 44;
    return chagePower;
  }
}

//手机
class Phone {
  //充电方法
  charging(voltage: IVoltage5V) {
    const power = voltage.output5();

    if (power === 5) {
      console.log("可以充电");
    } else {
      console.log("电压异常,无法充电");
    }
  }
}

//客户端调用
class Client {
  constructor() {
    //手机实例
    const phone = new Phone();
    //适配器
    const voltage5V = new Voltage5V(new Voltage220V());
    //给手机充电
    phone.charging(voltage5V);
  }
}

new Client();

实现上也非常简单,适配器不再继承了,而是通过聚合的方式获取到被适配者。

其实仔细一品,这种方式好像非常符合我们的日常生活,我们要给手机充电,那必然会有一个电源适配器,这个电源适配器又需要一个插座来获取电,然后适配器再给手机充电。

注意事项

对象适配器和类适配器其实都算是同一种思想,只不过实现的方式不同,根据合成复用原则,使用聚合代替继承,所以它解决了类适配器必须继承被适配者的局限性。

接口适配器

接口适配器其实是属于java这种强类型才能使用的一种方式,因为他们可以new出抽象类,并覆写方法,这在typescript中是无法实现了,js就更不行了,所以只做简单了解。

接口适配器有时候又被称为“缺省适配器”;当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现这个接口,并为每个接口实现一个默认方法,也就是空方法,不做任何处理,那么该抽象类的子类可以有选择性的覆写自己需要的方法来实现需求。

其实就是简单来说,我有一个接口,接口可能有20个方法,我的抽象类则实现这个接口,我写了20个空函数来做具体实现。

然后呢,子类去继承抽象时,根据需要去覆写个别空函数就行了。

下面是一个伪代码,无法实现的。

//接口
interface A {
  a1(): void;
  a2(): void;
  a3(): void;
  a4(): void;
}

//抽象类
abstract class B implements A {
  a1() {}
  a2() {}
  a3() {}
  a4() {}
}


//客户端
class Client  {
  constructor() {
    //接口适配器:java这种才能这么干
    const C = new B() {
      a1() {
        console.log("覆写a1")
      }
    }
  }
}

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

关于作者

站点职位 博主
获得点赞 0
文章被阅读 332

相关文章