装饰器模式:有时我们希望给某个对象而不是整个类添加一些功能,对对象进行添加新功能,而且不改变原有的结构和功能,这个时候我们就应该想到装饰器模式。
先看一个例子
我们可以穿各种各样的衣服,而且进行各种组合,假如要你设计一下这个模式,你会如何设计了。
我们来看一下使用装饰器模式的UML类图吧。
这个类图是比较复杂的,我们先来解释一下。
最顶层的一个类,appearance是所有类的父类,它里面只有一个show函数(在JS里面可以是一个空函数)
Person类和Decorate类继承于Appearance类,其中Decorate类中,我们有一个component属性,还有一个与之配套的设置函数setComponent
TShirts Jeans Phone还有可以有其他衣服 都继承于Decorate
我们每生成一件类型的衣服 然后给它设置component 然后就相当于穿上了,当调用show函数时,类似于递归调用,具体详情可以看代码。
class Appearance {
show () {}
}
class Person extends Appearance {
constructor() {
super();
}
show () {
console.log("我要穿衣服了!")
}
}
class Decorate extends Appearance {
constructor() {
super();
this.component = null;
}
setDecorate(component) {
this.component = component;
}
show() {
if (this.component !== null) {
this.component.show();
}
}
}
class TShirts extends Decorate {
constructor() {
super();
}
show () {
console.log(this.component);
this.component.show();
console.log("穿上TShirts")
}
}
class Jeans extends Decorate {
constructor() {
super();
}
show () {
this.component.show();
console.log("穿上Jeans")
}
}
class Phone extends Decorate {
constructor() {
super();
}
show () {
this.component.show();
console.log("带上手机")
}
}
const cyl = new Person();
const ts = new TShirts();
const je = new Jeans();
const ph = new Phone();
ts.setDecorate(cyl);
je.setDecorate(ts);
ph.setDecorate(je);
ph.show();
很完美的实现,因为这样的实现满足开发封闭原则,当我们想增加衣服时,生成对应的实例然后调用setComponent方法即可。
那么在前端中,是否也需要这样复杂的设计了?很显然,我们应该对设计进行简化。
我们以画图为例,看一下一个简单版的UML类图。
class Circle {
draw() {
console.log("画圆");
}
}
class Decorator {
constructor(circle) {
this.circle = circle;
}
draw() {
this.circle.draw();
this.setRedBorder();
}
setRedBorder() {
console.log("加上红色的边框")
}
}
const c = new Circle();
const d = new Decorator(c);
d.draw();
这个非常简单,理解起来不难。
接下来我们就来讨论ES7的decorator(装饰器)
首先我们安装一个插件"babel-plugin-transform-decorators-legacy",在.babelrc配置一下。
然后就可以运行代码了。
@demoDec
class Demo {}
function demoDec(target) {
target.flag = true;
}
console.log(Demo.flag);
上面代码怎么理解了,为什么相当于装饰器了。上面代码可以翻译成这个代码
Demo = demoDec(Demo) || Demo;
上面是对类的装饰,当然还可以对方法进行装饰。
function readOnly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Demo {
@readOnly
name() {
return "cyl"
}
}
const d = new Demo();
d.name = () => {};
console.log(d.name());
我们使得Demo里面的name方法不允许被修改,然后运行结果如下
即使在运行过程中 name函数被修改了,但是依然是原来的函数。
对装饰器的部分解释
- 装饰器第一个参数是 类的原型对象,上例是 Demo.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);
- 第二个参数是 所要装饰的属性名
- 第三个参数是 该属性的描述对象
回归主题,适配器模式的主要功能是给某个对象而不是整个类添加一些功能,对对象进行添加新功能,而且不改变原有的结构和功能。
ES7的语法很方便的帮助我们实现了,但是我们还是需要理解原理。在实际开发过程中,比如react的connect函数就可以使用装饰器,再比如core-decorators.js中为我们实现很多十分常用的装饰器(readOnly ,log等)。
最后我们怀着期待迎接ES7的Decorator吧!
大家可以看左边个人分类的目录,跟着一起学习。