# 装饰器模式
# 介绍
- 为对象添加新功能
- 不改变其原有的结构和功能
与适配器不同,适配器是旧接口已经不能用了,或者说数据,数据格式不能用,所以需要重新设计一个去用,而装饰器模式是原有的功能我们还会继续用,在其基础上再添加功能
# UML类图
# 代码演示
// 被装饰者
class Circle {
draw() {
console.log('画一个圆形')
}
}
// 装饰器
class Decorator {
constructor(circle) {
this.circle = circle
}
setRedBorder(circle) {
console.log('设置红色边框')
}
draw() {
this.circle.draw()
this.setRedBorder(circle)
}
}
// Test
let circle = new Circle()
circle.draw()
let decorator = new Decorator()
decorator.draw()
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
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
# 场景
- ES7装饰器
- 第三方库 core-decorators
# ES7 装饰器解析
- 配置环境
npm install babel-plugin-transform-decorators-legacy --save-dev
.babelrc
{
"presets": [
"es2015",
"latest"
],
"plugins": [
"transform-decorators-legacy"
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 装饰类
// 例1:
// 对Demo类进行修饰
@testDec
class Demo {
// ...
}
function testDec(target) {
target.isDec = true
}
console.log(Demo.isDec) // true
// 可以加参数
// 装饰器
function testDec(isDec) {
return function(target) {
target.isDec = isDec
}
}
// Demo - 被装饰者
@testDec(false)
class Demo {
}
console.log(Demo.isDec) // false
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
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
// 例2: mixin示例
function mixins(...list) {
return function(target) {
Object.assign(target.prototype, ...list)
}
}
const Foo = {
foo() {
console.log('foo')
}
}
@mixins(Foo)
class MyClass {
}
let obj = new MyClass()
obj.foo() // 'foo'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 装饰方法
// 例1:
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属性的writable装饰为false
name() {
return `${this.first} - ${this.last}`
}
}
var p = new Person()
console.log(p.name()) // A - B
// p.name = function() {} // 这里会报错,是因为 name函数被装饰为只读属性
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
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
// 例2:
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 // oldValue → add() 经过装饰之后,会先打印日志,然后执行oldValue,也就是add()
add(a, b) {
return a + b
}
}
const math = new Math()
const result = math.add(2, 4) // 执行add时,会自动打印日志,因为add已经被log装饰器装饰过
console.log(result)
/*
Result:
Calling add with Arguments(2) [2, 4, callee: (...), Symbol(Symbol.iterator): ƒ]
6
*/
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
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
# core-decorators
- 第三方开源依赖
- 提供常用的装饰器
- 示例
// 首先安装 npm i core-decorators --save
// 开始编码
import { readonly } from 'core-decorators'
class Person {
@readonly
name() {
return 'zhang'
}
}
let p = new Person()
console.log(p.name())
// p.name = function() {} // 这里会报错,是因为 name函数被装饰为只读属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// deprecate 执行前有警告提示
import { deprecate } from 'core-decorators'
class Person {
@deprecate
facepalm(){}
@deprecate('We stopped facepalming')
facepalmHard(){}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 设计原则验证
装饰器模式将现有对象和装饰器进行分离,两者是独立存在的,装饰的时候不去修改原有的对象,符合开放封闭原则