@TOC
装饰器模式
- 介绍
- 演示
- 场景
- 总结
装饰器模式 介绍
-
为对象添加新功能
-
不改变其原有的结构和功能
概念
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
示例
手机上套一个壳可以保护手机,壳上粘一个指环,可以挂在手指上不容易滑落,这就是一种装饰。手机还是那个手机,手机的功能一点都没变,只是在手机的外面装饰了一些其他附加的功能。日常生活中,这样的例子非常多。
装饰器模式 演示
通常的 UML 类图
去掉父子类的继承(前端少用),简化之后的类图是
代码演示
class Circle {
draw() {
console.log('画一个原型')
}
}
class Decorator {
constructor(circle) {
this.circle = circle
}
draw() {
this.circle.draw()
this.setRedBoder(circle)
}
setRedBoder(circle) {
console.log('设置红色边框')
}
}
// 测试代码
let circle = new Circle()
circle.draw()
let dec = new Decorator(circle) // 装饰
dec.draw()
结合本示例,联想一下开始介绍的手机壳,是一个道理,都是外面加一层装饰,且不改变原有功能。
装饰器模式 场景
就 ES7 标准中增加的装饰器来体会一下装饰器模式在 JS 中的应用。
如果node -v是6.x版本的话,要安装npm i babel-plugin-transform-decorators-legacy --save-dev,然后修改.babrlrc
{
"presets": ["es2015", "latest"],
"plugins": ["transform-decorators-legacy"]
}
另外注意,装饰器是在编译时执行的而不是运行时。
ES7 装饰器
参考 es6.ruanyifeng.com/#docs/decor…
装饰类
先看一个最简单的 demo ,体会一下装饰器
// 一个简单的 demo
@testDec
class Demo {
// ...
}
function testDec(target) {
target.isDec = true;
}
alert(Demo.isDec) // true
可以加参数
// 可以加参数
function testDec(isDec) {
return function(target) {
target.isDec = isDec;
}
}
@testDec(true)
class Demo {
// ...
}
alert(Demo.isDec) // true
装饰器的原理
// 装饰器的原理
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
最后一个示例
function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list)
}
}
const Foo = {
foo() { alert('foo') }
}
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // 'foo'
装饰方法
先看一个 readonly 的 demo
function readonly(target, name, descriptor){
// descriptor 属性描述对象(Object.defineProperty 中会用到),原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}
class Person {
constructor() {
this.first = 'A'
this.last = 'B'
}
// 装饰方法
@readonly
name() { return `${this.first} ${this.last}` }
}
var p = new Person()
console.log(p.name())
// p.name = function () {} // 这里会报错,因为 name 是只读属性
再看一个例子,加一个装饰器自动打印日志
function log(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function() {
console.log(`Calling ${name} with`, arguments);
return oldValue.apply(this, arguments);
};
return descriptor;
}
class Math {
// 装饰方法
@log
add(a, b) {
return a + b;
}
}
const math = new Math();
const result = math.add(2, 4); // 执行 add 时,会自动打印日志,因为有 @log 装饰器
console.log('result', result);
core-decorators
-
第三方开源lib
-
提供常用的装饰器
-
查阅文档:core-decorators
core-decorators.js 是一个第三方模块,提供了几个常见的修饰器,通过它可以更好地理解修饰器。
用之前肯定得先安装npm i core-decorators --save,然后先用它来实现上述的 readonly
// 首先安装 npm i core-decorators --save
// 开始编码:
import { readonly } from 'core-decorators'
class Person {
@readonly
name() {
return 'zhang'
}
}
let p = new Person()
alert(p.name())
// p.name = function () { /*...*/ } // 此处会报错
在看一个常用的例子,对已经弃用的协议,给出警告。
import { deprecate } from 'core-decorators';
class Person {
@deprecate
facepalm() {}
@deprecate('We stopped facepalming')
facepalmHard() {}
@deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })
facepalmHarder() {}
}
let person = new Person();
person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.
person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming
person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
// See http://knowyourmeme.com/memes/facepalm for more details.
具体的使用,可再去参考官网。
装饰器模式 总结
- 什么是装饰器模式
- 使用场景
- 核心:不改变原来的,增加装饰
- ES7 装饰器
设计原则验证:
- 将现有对象和装饰器进行分离,两者独立存在
- 符合开放封闭原则