装饰器模式是一种可以为函数或类增添特性的代码实现方式。
可以在不修改原有对象的基础上,为其增添新的属性和方法。
网上有一个很好的例子解释为什么需要装饰器模式。假设有一个自行车商行,它要求店内的自行车根据不同的配件售卖不同的价格,配置项包括前灯,尾灯,铃铛,修理扳手等,可以选择一种或者几种自由组合。
试想一下组合结果有几十上百中,每增加一种配件组合方式都是恐怖的。如果用类来实现意味着要为每一种组合定义一个类型。这明显不科学。
针对这种情况就可以使用装饰器来解决这个问题。装饰器类似于组合,针对每一个配件定义一个类,比如无配件的自行车是一个类,前灯是一个类,尾灯是一个类。这三个组合到一起就是一台具有前灯和尾灯的类。如果只需要尾灯配件就将自行车基类和尾灯类组合。
所以这里需要两个类对象,一个是自行车的基类Bicycle就是具备自行车基本功能的类,第二个是用不组合的装饰器类BicycleDecotator
自行车鸡肋中包含基本的ride方法,以及基本售价也就是没有任何配件的售价。

// 自行车基类
class Bicycle {
   // 其它方法
   ride () {
       console.log('骑行方法');
   }
   getPrice() {
       // 售价100
       return 100;
   }
}

创建一个装饰者模式基类,这个基类其实没有做什么事情,它只是接受一个Bicycle实例,实现其对应的方法,并且将调用其方法返回而已。

class BicycleDecotator {
   constructor(bicycle) {
       this.bicycle = bicycle;
   }
   ride () {
       return this.bicycle.ride();
   }
   getPrice() {
       return this.bicycle.getPrice();
   }
}

有了这个基类之后,我们就可以根据我们的需求对原来的Bicycle类为所欲为了。比如我可以创建一个添加了前灯的装饰器。这个装饰器在原本的价格上增加10元。

class HeadLightDecorator extends BicycleDecorator {
   constructor(bicycle) {
       super(bicycle);
   }
   getPrice() {
       return this.bicycle.getPrice() + 10;
   }
}

还可以创建一个添加了尾灯的装饰器,同样价格上增加10元。

class TailLightDecorator extends BicycleDecorator {
   constructor(bicycle) {
       super(bicycle);
   }
   getPrice() {
       return this.bicycle.getPrice() + 20;
   }
}

那么,接下来我们就可以来对其自由组合了, bicycle实例是基本款自行车,售价10元。

let bicycle = new Bicycle(); // 原始自行车
console.log(bicycle.getPrice()); // 100

将基本款和前灯进行组合,就是创建HeadLightDecorator实例,传入bicycle实例。

const headbicycle = new HeadLightDecorator(bicycle);
console.log(headbicycle.getPrice());  // 110

将添加前灯的自行车和尾灯组合就是前灯和尾灯都具备的自行车。

const tailbicycle = new TailLightDecorator(headbicycle);
console.log(tailbicycle.getPrice()); // 120

这样写的好处是假设说我们有10个配件,那么我们只需要写10个配件装饰器,然后就可以任意搭配成不同配件的自行车并计算价格。而如果是按照子类的实现方式的话,10个配件可能就需要有几百个甚至上千个子类了, 听上去都很恐怖。
装饰者模式的使用场合:
如果你需要为类增添特性或职责,可是从类派生子类的解决方法并不太现实的情况下,就应该使用装饰者模式。
在例子中,我们并没有对原来的Bicycle基类进行修改,因此也不会对原有的代码产生副作用。我们只是在原有的基础上增添了一些功能。因此,如果想为对象增添特性又不想改变使用该对象的代码的话,则可以采用装饰者模式。
或许你早就已经在用这种方式了,只是你并不知道这就是装饰器。前端常用的节流和防抖其实即使装饰器模式,只是在js中我们习惯叫他高阶函数而已。

function throttle(func, delay) {
   const self = this;
   let tid;
   return function(...args) {
       if (tid) return;
       tid = setTimeout(() => {
           func.call(self, ...args);
           tid = null;
       }, delay);
   }
}
function debounce(func, delay) {
   const self = this;
   let tid;
   return function(...args) {
       if (tid) clearTimeout(tid);
       tid = setTimeout(() => {
           func.call(self, ...args);
           tid = null;
       }, delay);
   }
}

[参考来源]

  1. 菜鸟教程-装饰器模式
  2. 博客园-陈陈jg

转载须知

如转载必须标明文章出处文章名称文章作者,格式如下:

转自:【致前端 - https://madaozhijian.com】 装饰器模式  "隐冬"