装饰者模式是一种结构型设计模式,目的是为了促进代码复用。装饰者模式与Mixin很相似,可以算是一种子类化的替代方案。
装饰者模式通常是为了添加系统的现有能力,为系统中的对象添加额外的功能,同时不影响到系统底层代码(这些额外能力父类可能并不需要,只有部分子类需要)
简单实现
// 场景描述:当你购买macbook时,可以通过cost方法来获得最终的价格,
// 我们通过装饰者来扩展cost方法,当增加不同配置时,扩展cost计算方法
// macbook类
function MacBook (screenSize) {
this.cost = function (){
if (screenSize == 13) {
return 999
} else {
return 1199
}
}
}
// 装饰者1:增加内存配置,费用上升100
function Memory (macbook) {
var v = macbook.cost();
macbook.cost = function () {
return v + 100
}
}
// 装饰者2:增加硬盘容量,费用上升250
function HardDiscCapacity (macbook) {
var v = macbook.cost();
macbook.cost = function () {
return v + 250
}
}
// 装饰者3:延长质保期,费用上升120
function Insurance (macbook) {
var v = macbook.cost();
macbook.cost = function () {
return v + 120
}
}
// 使用装饰者
var mb = new MacBook(13)
Memory(mb)
HardDiscCapacity(mb)
Insurance(mb)
console.log(mb.cost())
// 输出1469
//装饰者只影响需要装饰的实例
var mb2 = new MacBook(13)
console.log(mb2.cost())
// 仍然输出999
在上面的例子中,装饰者重写了MacBook类的cost方法。相比直接修改MacBook的cost方法,这样的写法使我们的代码更易读,也更佳健壮。
优点
对象可以被新的行为包装或装饰,从而继续使用,并且不用担心被使用的基本对象被修改,在一些场景下,我们可以减少需要大量子类才能实现的开发复杂度。
缺点
过度放开任意扩展,会导致对象复杂变得难以管理,不熟悉这个模式的开发人员也会难以理解为何如此使用。
ES7 Decorator
es7里的Decorator(@),借鉴于python,可用于对类、方法进行装饰(不可对函数进行装饰)。通过使用Decorator可以达到装饰者模式、Mixin模式等的效果,如下:
// Mixin.js
function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list);
};
}
// 使用mixin
// 需要混入的方法
const Foo = {
foo() { console.log('foo') }
};
// 通过decorator混入
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
// 使用decorator实现一个事件触发记录器,同时能够重用
function logger(topic) {
return function(target, name, descriptor) {
const fn = descriptor.value;
descriptor.value = function() {
console.log('before' + topic)
let value = fn.apply(this, arguments);
console.log(value)
console.log('after' + topic)
};
};
}
class FooComponent {
@logger('HoldMessage')
holdMessage() {
return { my: 'data' };
}
@logger('KeepMessage')
keepMessage() {
return { my: 'data2' };
}
}
let foo = new FooComponent();
foo.holdMessage();
// beforeHoldMessage
// { my: 'data' }
// afterHoldMessage
具体Decorator语法和使用场景,不在本篇中进行详细描述,请大家参考ES6相关的文章