首页 设计模式 05 — 装饰者模式
文章
取消

设计模式 05 — 装饰者模式

1. 定义

《JavaScript 设计模式与开发实践》中对装饰者模式的描述如下:

这种给对象动态地增加职责的方式称为装饰者(decorator)模式。装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对象动态地添加职责。跟继承相比,装饰者是一种更轻便灵活的做法,这是一种“即用即付”的方式。

2. 案例

设计模式 03 — 策略模式中使用了策略模式重构了 submit 函数:

JS

1
2
3
4
5
var submit = function (meeting) {
  var msg = validateMeeting(meeting);
  if (msg) return console.log(msg);
  // 验证通过,发起请求...
};

虽然校验逻辑都被封装到了 validateMeeting 函数中,但 submit 函数内部还是要调用 validateMeeting

在使用装饰者模式重构 submit 前,先把 validateMeetingsubmit 中抽离出来,让他们不再有任何耦合关系

JS

1
2
3
4
5
6
7
var submit = function (meeting) {
  // 发起请求...
};

var validateMeeting = function (meeting) {
  // 校验逻辑...
}

然后再考虑如何给 submit 函数动态添加 validateMeeting 函数的功能,但是:

在 JavaScript 中可以很方便地给某个对象扩展属性和方法,但却很难在不改动某个函数源代码的情况下,给该函数添加一些额外的功能。

JavaScript 中通常使用 AOP 装饰函数。

3. 使用AOP装饰函数

AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后,再通过“动态织入”的方式掺入业务逻辑模块中。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。

JavaScript 中通常使用高阶函数实现 AOP :

JS

1
2
3
4
5
6
7
8
var before = function( fn, beforeFn ) {
  return function() {
    // 运行前一个函数
    var msg = beforeFn.apply(this, arguments);
    // 根据返回值判断是否调用后一个函数
    msg ? console.log(msg) : fn.apply(this, arguments);
  }
}

然后再使用 before 函数装饰 submit 函数:

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var submit = function (meeting) {
  // 发起请求...
  console.log('提交表单');
};

var validateMeeting = function (meeting) {
  // 模拟校验逻辑
  var num = Math.random();
  return num > 0.5 ? '验证不通过' : null;
}

// 使用AOP装饰函数
submit = before(submit, validateMeeting);

// TEST
submit({});

4. AOP装饰函数的优点

用 AOP 装饰函数的技巧在实际开发中非常有用。不论是业务代码的编写,还是在框架层面,我们都可以把行为依照职责分成粒度更细的函数,随后通过装饰把它们合并到一起,这有助于我们编写一个松耦合和高复用性的系统。

本文由作者按照 CC BY 4.0 进行授权