JS模式之装饰器模式

装饰器模式

在装饰器模式中,可以在运行时动态添加附加功能到对象中。当处理静态类,这可能是一个挑战。在JS中,由于对象是可变的,因此,添加功能到对象中的过程本身并不是问题。

装饰者模式的一个比较方便的特征在于其预期行为的可定制和可配置特性。可以从仅具有一些基本功能的普通对象开始,然后从可用装饰资源池中选择需要用于增加普通对象的那些功能,并且按照顺序进行装饰;

一个例子

如果实现一个销售商品的价格,有原始价格、国税、地税以及币种的转换;

1
2
3
4
5
var sale = new Sale(100);
sale.decorate('fedax');
sale.decorate('quebec');
sale.decorate('money');
sale.getPrice()

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function Sale(price) {
this.price = (price > 0) || 100;
this.decorators_list = [];
}
Sale.decorators = {};
Sale.decorators.fedtax = {
getPrice: function(price) {
return price + price*5/100;
}
};
Sale.decorators.quebec = {
getPrice: function(price) {
return price + price*7.5/100;
}
}
Sale.decorators.money = {
getPrice: function(price) {
return "$" + price.toFixed(2);
}
}
Sale.prototype.decorate = function(decorator) {
this.decorators_list.push(decorator);
}
Sale.prototype.getPrice = function() {
var price = this.price,
i,
max = this.decorators_list.length,
name;
for(i = 0; i < max; i += 1) {
name = this.decorators_list[i];
price = Sale.decorators[name].getPrice(price);
}
return price;
}

ECMASCRIPT装饰器

在ES8中,将会添加新的语言功能-装饰器;

新的功能目前已经处于stage2阶段,目前只可作用于class和class属性;

class member decorator

装饰器函数接受三个参数:

  • target - 所属的class
  • name - member的名字
  • descriptor - member的描述对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Example {
a() {}
@readonly
b() {}
}
const e = new Example();
e.a = 1;
e.b = 2; // TypeError: Cannot assign to read only property 'b' of object '#

目前这个装饰器使用需要用babel插件才可以使用,浏览器都没有支持;

class decorator

直接看使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function log(Class) {
return (...args) => {
console.log(args);
return new Class(...args);
};
}
@log
class Example {
constructor(name, age) {
}
}
const e = new Example('Graham', 34); // [ 'Graham', 34 ]
console.log(e); // Example {}

其实新的装饰器跟python有点类似,有点区别;类装饰器跟python装饰器思路是一致,只有类属性装饰器是是通过重新定义属性描述器中的值来装饰的。