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