又称装饰器模式,在不改变原对象的基础上,通过对其添加属性和方法进行包装拓展,使得原对象可以动态具有更多功能。例如:装修毛胚房(添加居住功能)
解决问题:
- 不改变原对象,添加新功能
优点:
- 松耦合:装饰者和被装饰者
- 装饰者复用性强
- 符合开放-封闭原则
缺点:
- 复杂度增加,嵌套地狱
- 难定位错误和难维护
使用场景:
- 性能上报:记录某异步请求请求耗时的性能数据并上报
- 异常处理:可以对原有代码进行简单的异常处理
与其他设计模式区别:
- 装饰者模式:扩展功能,原功能不变
- 适配器模式:功能不变,只是换了数据格式
- 组合模式:管理组合对象和叶子对象,为它们提供一致的操作给客户端
- 策略模式:只有一层选择,选择某个功能
window.onload = function() {
console.log('原先的 onload 事件 ')
}
/* 发送埋点信息 */
function sendUserOperation() {
console.log('埋点:用户当前行为路径为 ...')
}
/* 将新的功能添加到 onload 事件上 */
window.onload = function() {
var originOnload = window.onload
return function() {
originOnload && originOnload()
sendUserOperation()
}
}()
// 输出: 原先的 onload 事件
// 输出: 埋点:用户当前行为路径为 ...
面向切面编程(AOP):是对 OOP 的补充,在各个节点前后插入动作。
与装饰者模式区别: 装饰者模式是一种设计模式,而AOP是一种编程范式,可以看作是装饰者模式的实现。
与OOP区别:
- OOP:针对业务处理过程的实体及其属性和行为进行抽象封装
- AOP:针对业务处理过程中的某个步骤或阶段进行提取
例如:雇员抽象为Employee类(OOP),雇员上班(是一个AOP节点,上班前坐车和下班后吃饭是AOP的提取工作)
// 原函数
var takePhoto = function(){
console.log('拍照片');
}
Function.prototype.before = function(beforefn){
let _self = this;
return function(){
beforefn.apply(this, arguments);
return _self.apply(this, arguments);
}
}
Function.prototype.after = function(afterfn){
let _self = this;
return function(){
let ret = _self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
}
// 用装饰函数装饰原函数
takePhoto = takePhoto.before(() => {
console.log('加滤镜')
}).after(() => {
console.log('加滤镜')
})
takePhoto()
let takePhoto = function () {
console.log('拍照片');
}
// 给 takePhoto 添加属性 after
Object.defineProperty(takePhoto, 'after', {
writable: true,
value: function () {
console.log('加滤镜');
},
});
// 给 takePhoto 添加属性 before
Object.defineProperty(takePhoto, 'before', {
writable: true,
value: function () {
console.log('打开相机');
},
});
// 包装方法
let aop = function (fn) {
return function () {
fn.before()
fn()
fn.after()
}
}
takePhoto = aop(takePhoto)
takePhoto()
class Test {
takePhoto() {
console.log('拍照');
}
}
function around(target, action, fn) {
let old = target.prototype[action];
if (old) {
target.prototype[action] = function () {
let self = this;
let handle = () => {
return old.apply(self, arguments || args);
}
fn.bind(self);
fn(handle);
}
}
}
around(Test, 'takePhoto', (handle) => {
console.log('准备拍照');
let res = handle();
console.log('拍照完成');
return res;
});
let t = new Test();
t.takePhoto();
function after(target, key, desc) {
const { value } = desc;
desc.value = function (...args) {
let res = value.apply(this, args);
console.log('加滤镜')
return res;
}
return desc;
}
class Test{
@after
takePhoto(){
console.log('拍照')
}
}
let t = new Test()
t.takePhoto()
5 分钟即可掌握的 JavaScript 装饰者模式与 AOP JavaScript 设计模式精讲 - 第三章 15装饰者模式