使用装饰者模式做有趣的事情

什么是装饰者模式

装饰者模式是一种为函数或类增添特性的技术,它可以让我们在不修改原来对象的基础上,为其增添新的能力和行为。它本质上也是一个函数(在javascipt中,类也只是函数的语法糖)。

我们什么时候可以弄到它呢

我们来假设一个场景,一个自行车商店有几种型号的自行车,现在商店允许用户为每一种自行车提供一些额外的配件,比如前灯、尾灯、铃铛等。每选择一种或几种配件都会影响自行车的售价。

如果按照比较传统的创建子类的方式,就等于我们目前有一个自行车基类,而我们要为每一种可能的选择创建一个新的类。可是由于用户可以选择一种或者几种任意的配件,这就导致最终可能会生产几十上百个子类,这明显是不科学的。然而,对这种情况,我们可以使用装饰者模式来解决这个问题。

自行车的基类如下:

class Bicycle { // 其它方法 wash () {} ride () {} getPrice() { return 200; } }

那么我们可以先创建一个装饰者模式基类

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

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

有了这个基类之后,我们就可以根据我们的需求对原来的Bicycle类为所欲为了。比如我可以创建一个添加了前灯的装饰器以及添加了尾灯的装饰器:

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

那么,接下来我们就可以来对其自由组合了:

let bicycle = new Bicycle(); console.log(bicycle.getPrice()); // 200 bicycle = new HeadLightDecorator(bicycle); // 添加了前灯的自行车 console.log(bicycle.getPrice()); // 220 bicycle = new TailLightDecorator(bicycle); // 添加了前灯和尾灯的自行车 console.log(bicycle.getPrice()); // 240

这样写的好处是什么呢?假设说我们有10个配件,那么我们只需要写10个配件装饰器,然后就可以任意搭配成不同配件的自行车并计算价格。而如果是按照子类的实现方式的话,10个配件可能就需要有几百个甚至上千个子类了。

从例子中我们可以看出装饰者模式的适用场合:

如果你需要为类增添特性或职责,可是从类派生子类的解决方法并不太现实的情况下,就应该使用装饰者模式。

在例子中,我们并没有对原来的Bicycle基类进行修改,因此也不会对原有的代码产生副作用。我们只是在原有的基础上增添了一些功能。因此,如果想为对象增添特性又不想改变使用该对象的代码的话,则可以采用装饰者模式。

装饰者模式除了可以应用在类上之外,还可以应用在函数上(其实这就是高阶函数)。比如,我们想测量函数的执行时间,那么我可以写这么一个装饰器:

function func() { console.log('func'); } function timeProfileDecorator(func) { return (...args) => { const startTime = new Date(); func.call(this, ...args); const elapserdTime = (new Date()).getTime() - startTime.getTime(); console.log(`该函数消耗了${elapserdTime}ms`); } } const newFunc = timeProfileDecorator(func); console.log(newFunc()); 做一些有趣的事情

既然知道了装饰者模式可以在不修改原来代码的情况下为其增添一些新的功能,那么我们就可以来做一些有趣的事情。

我们可以为一个类的方法提供性能分析的功能。

class TimeProfileDecorator { constructor(component, keys) { this.component = component; this.timers = {}; const self = this; for (let i in keys) { let key = keys[i]; if (typeof component[key] === 'function') { this[key] = function(...args) { this.startTimer(key); // 解决this引用错误问题 component[key].call(component, ...args); this.logTimer(key); } } } } startTimer(namespace) { this.timers[namespace] = new Date(); } logTimer(namespace) { const elapserdTime = (new Date()).getTime() - this.timers[namespace].getTime(); console.log(`该函数消耗了${elapserdTime}ms`); } } // example class Test { constructor() { this.name = 'cjg'; this.age = 22; } sayName() { console.log(this.name); } sayAge() { console.log(this.age); } } let test1 = new Test(); test1 = new TimeProfileDecorator(test1, ['sayName', 'sayAge']); console.log(test1.sayName()); console.log(test1.sayAge()); 对函数进行增强

节流函数or防抖函数

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); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyydys.html