设计模式-装饰者模式

基本概念

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class –wiki
在面相对象编程范式中,装饰器模式是一种可以在不影响同一个类的其他实例对象的行为的前提下,给一个独立的对象动态添加行为的设计模式

这里可以看出装饰器模式主要应用于解决扩展独立的实例对象的行为的场景。

以上比较抽象,这里我们可以提出一个具体的需求

假如我们在线上已经有一个User类,实例化了一些对象,工作的很好,但是产品提出了新的需求,需要添加一个付费用户,具体代码如下

1
2
3
4
5
6
7
8
9
10
11
12
class User {
constructor(name, score) {
this.name = name
this.score = score
}

increment() {
this.score++
}
}

const user1 = new User('Tom', 1)

那么,我们可以通过什么方法扩展一个对象?

  1. 直接修改
  2. 创建一个子类继承父类,用子类实例化新对象
  3. 不改变原对象,在原对象基础上进行“装饰”,增加功能

代码如下:

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
// 方法1
const paidUser1 = new User("Tom", 1);
paidUser1.accountBalance = 1;
paidUser1.increaseBalance = function () {
this.accountBalance++;
};

// 方法2
class PaidUser extends User {
constructor(paidName, paidScore, accountBalance) {
super(paidName, paidScore);
this.accountBalance = accountBalance;
}

increaseBalance() {
this.accountBalance++;
}
}

const paidUser2 = new PaidUser("Bob", 1, 1);

// 方法3
function paid(accountBalance) {
return function (user) {
user.accountBalance = accountBalance;
user.increaseBalance = function () {
this.accountBalance++;
};
return user;
};
}

const user3 = new User("Jack", 1);
paid(0)(user3);

使用场景

  • Form组件,使用装饰器在用户提交前进行校验
  • 使用装饰器记录执行开始及结束事件并计算消耗时间
  • 用户点击按钮时使用装饰器将数据发送至后端统计点击次数
  • 使用装饰器防抖或节流

AOP(Aspect-oriented programming)-面相切面编程

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
const AOP = {};
AOP.before = function (fn, before) {
return (...args) => {
before(...args)
fn(...args)
};
};
AOP.after = function (fn, after) {
return function () {
fn.apply(this, arguments);
after.apply(this, arguments);
};
};

function submit() {
console.log("提交数据");
}

document.querySelector(".btn").onclick = submit;

// 使用AOP进行装饰

function submit() {
console.log(this);
console.log("提交数据");
}
function check() {
console.log(this);
console.log("进行校验");
}
submit = AOP.before(submit, check);
document.querySelector(".btn").onclick = submit;
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class App {
constructor() {
this.title = "Home page";
}

render() {
console.log("渲染页面" + this.title);
}
}

const logWrapper = (targetClass) => {
const originRender = targetClass.prototype.render;
targetClass.prototype.render = function () {
console.log("before render");
originRender.apply(this);
console.log("after render");
};
return targetClass;
};

const LogWrapperApp = logWrapper(App);
const logApp = new LogWrapperApp();
logApp.render();

// 使用提案阶段的装饰器修饰符
@logWrapper
class App {
...
}

// 上面是直接修改class,还可以修改单独属性
const logWrapper = (target, name, descriptor) => {
const originRender = descriptor.value;
descriptor.value = function () {
console.log("before render");
originRender.apply(this);
console.log("after render");
};
console.log("target", target);
};


class App {
constructor() {
this.title = "Home page";
}

@logWrapper
render() {
console.log("渲染页面" + this.title);
}
}

new App().render();