Adapter Design Pattern,将类的接口转换为客户期待的另外一个接口,使接口不兼容的对象能够相互合作。

在原有的接口或类的外层封装一个新的适配器层
1_LF_LoN1fWL87P63Fn-hMgw.png

使用场景

  1. 原有接口功能不满足现有需求,在兼容老接口的同时做适当扩展
  2. 相似接口间做功能的统一
  3. 不同数据格式、不同协议需要转换
  • when new components need to be integrated and work together with existing components in the application
  • parts of the program are rewritten with an improved interface, but the old code still expects the original interface
  • when you have a third party API that there is a chance it changes ```javascript // 假设你有两个接口相互兼容的类:圆孔(Round­Hole)和圆钉(Round­Peg)。 class RoundHole is constructor RoundHole(radius) { … }

    method getRadius() is

    1. // 返回孔的半径。

    method fits(peg: RoundPeg) is

    1. return this.getRadius() >= peg.getRadius()

class RoundPeg is constructor RoundPeg(radius) { … }

  1. method getRadius() is
  2. // 返回钉子的半径。

// 但还有一个不兼容的类:方钉(Square­Peg)。 class SquarePeg is constructor SquarePeg(width) { … }

  1. method getWidth() is
  2. // 返回方钉的宽度。

// 适配器类让你能够将方钉放入圆孔中。它会对 RoundPeg 类进行扩展,以接收适 // 配器对象作为圆钉。 class SquarePegAdapter extends RoundPeg is // 在实际情况中,适配器中会包含一个 SquarePeg 类的实例。 private field peg: SquarePeg

  1. constructor SquarePegAdapter(peg: SquarePeg) is
  2. this.peg = peg
  3. method getRadius() is
  4. // 适配器会假扮为一个圆钉,
  5. // 其半径刚好能与适配器实际封装的方钉搭配起来。
  6. return peg.getWidth() * Math.sqrt(2) / 2

// 客户端代码中的某个位置。 hole = new RoundHole(5) rpeg = new RoundPeg(5) hole.fits(rpeg) // true

small_sqpeg = new SquarePeg(5) large_sqpeg = new SquarePeg(10) hole.fits(small_sqpeg) // 此处无法编译(类型不一致)。

small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg) large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg) hole.fits(small_sqpeg_adapter) // true hole.fits(large_sqpeg_adapter) // false

  1. ```javascript
  2. /**
  3. * The Target defines the domain-specific interface used by the client code.
  4. */
  5. class Target {
  6. public request(): string {
  7. return 'Target: The default target\'s behavior.';
  8. }
  9. }
  10. /**
  11. * The Adaptee contains some useful behavior, but its interface is incompatible
  12. * with the existing client code. The Adaptee needs some adaptation before the
  13. * client code can use it.
  14. */
  15. class Adaptee {
  16. public specificRequest(): string {
  17. return '.eetpadA eht fo roivaheb laicepS';
  18. }
  19. }
  20. /**
  21. * The Adapter makes the Adaptee's interface compatible with the Target's
  22. * interface.
  23. */
  24. class Adapter extends Target {
  25. private adaptee: Adaptee;
  26. constructor(adaptee: Adaptee) {
  27. super();
  28. this.adaptee = adaptee;
  29. }
  30. public request(): string {
  31. const result = this.adaptee.specificRequest().split('').reverse().join('');
  32. return `Adapter: (TRANSLATED) ${result}`;
  33. }
  34. }
  35. /**
  36. * The client code supports all classes that follow the Target interface.
  37. */
  38. function clientCode(target: Target) {
  39. console.log(target.request());
  40. }
  41. console.log('Client: I can work just fine with the Target objects:');
  42. const target = new Target();
  43. clientCode(target);
  44. console.log('');
  45. const adaptee = new Adaptee();
  46. console.log('Client: The Adaptee class has a weird interface. See, I don\'t understand it:');
  47. console.log(`Adaptee: ${adaptee.specificRequest()}`);
  48. console.log('');
  49. console.log('Client: But I can work with it via the Adapter:');
  50. const adapter = new Adapter(adaptee);
  51. clientCode(adapter);

参考资料

  1. 适配器模式
  2. 51 | 适配器模式:代理、适配器、桥接、装饰,这四个模式有何区别?
  3. Adapter Pattern 介紹及 JavaScript 實作
  4. Design Patterns - Adapter
  5. 适配器模式——兼容代码就是一把梭
  6. 22 | 适配器模式:如何处理不同 API 接口的兼容性?
  7. TypeScript 设计模式之适配器模式
  8. Adapter Design Pattern Implementation in Typescript