适配器模式在JavaScript中的应用:桥梁之间的旧与新

258 阅读3分钟

适配器模式是设计模式的一个有趣概念,其核心思想是为不兼容的接口提供一个中间层,使其能够无缝地工作。它如同为两个不同的插头提供一个转接头,让它们能够配合工作。在这篇博客中,我们将探讨如何在JavaScript中应用适配器模式,并通过一个实际的例子来加深理解。

什么是适配器模式?

适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换为客户希望的另外一个接口。适配器模式可以确保在不修改现有代码的前提下使原本由于接口不兼容而不能一起工作的类能够一起工作。

为什么要使用适配器模式?

  1. 代码复用:通过使用适配器,我们可以重用现有的代码,而无需进行大规模的重写或修改。
  2. 增加灵活性:适配器模式提供了一种机制,使得我们的系统更加灵活,并且可以与多种不同的系统或库交互。
  3. 平滑迁移:当引入新系统或进行系统升级时,适配器可以为我们提供一个过渡层,使得迁移变得更加平滑。

模式结构

适配器模式的UML图通常涉及以下几个核心组件:

  1. 目标(Target) :这是客户期望的接口。客户程序通过这个接口与适配器进行交互。
  2. 需要适配的类(Adaptee) :这是一个已经存在的、需要被适配的接口。
  3. 适配器(Adapter) :它负责加入目标接口和被适配的接口之间的桥梁。适配器实现目标接口并包含被适配的对象的实例,从而使两者能够协同工作。

截屏2023-08-23 09.26.42.png

JavaScript中的适配器模式实例

   var Target = function () {}

   Target.prototype.request = function () {
       throw new Error("子类必须实现此方法");
   };


   var Adapter = function (adaptee) {
       this.adaptee = adaptee
   }
   Adapter.prototype = Object.create(Target.prototype);
   Adapter.prototype.constructor = Adapter;
   Adapter.prototype.request = function () {
       this.adaptee.specialRequest()
   }


   var Adaptee = function () {}

   Adaptee.prototype.specialRequest = function () {
       console.log("specialRequest");
   };

   let adaptee = new Adaptee()
   let adapter = new Adapter(adaptee)
   adapter.request() // specialRequest

让我们分步骤解释这两行代码的意义:

  1. Adapter.prototype = Object.create(Target.prototype);

    这行代码的目的是为了建立原型链。使用 Object.create(Target.prototype) 会创建一个新对象,该对象的原型是 Target.prototype。这意味着任何由 Adapter 构造的实例都可以访问 Target.prototype 上定义的方法。这就是继承的核心:允许一个对象可以访问另一个对象的属性和方法。

  2. Adapter.prototype.constructor = Adapter;

    当我们重写 Adapter.prototype,我们实际上丢失了原始的 Adapter.prototype.constructor 的引用(它原本指向 Adapter 函数)。这个 constructor 属性通常用于引用创建当前对象的构造函数。当我们设置 Adapter.prototype = Object.create(Target.prototype); 之后,Adapter.prototype.constructor 就指向了 Target 而不是 Adapter。为了修复这个问题,我们显式地将 Adapter.prototype.constructor 重新设置为 Adapter

为什么我们需要关心 constructor 呢?虽然在日常的 JavaScript 编码中我们可能不经常用到它,但它在某些情况下可能很有用。例如,如果你想找出一个对象的构造函数(即它是由哪个函数创建的),你可以使用这个属性。除此之外,保持 constructor 属性的正确性也被认为是一个好的做法,因为其他的开发者可能会期望它正确地指向相应的构造函数。