Skip to content

Latest commit

 

History

History
185 lines (158 loc) · 4.31 KB

08装饰者模式.md

File metadata and controls

185 lines (158 loc) · 4.31 KB

装饰者模式

又称装饰器模式,在不改变原对象的基础上,通过对其添加属性和方法进行包装拓展,使得原对象可以动态具有更多功能。例如:装修毛胚房(添加居住功能)

解决问题:

  • 不改变原对象,添加新功能

优点:

  • 松耦合:装饰者和被装饰者
  • 装饰者复用性强
  • 符合开放-封闭原则

缺点:

  • 复杂度增加,嵌套地狱
  • 难定位错误和难维护

使用场景:

  • 性能上报:记录某异步请求请求耗时的性能数据并上报
  • 异常处理:可以对原有代码进行简单的异常处理

与其他设计模式区别:

  • 装饰者模式:扩展功能,原功能不变
  • 适配器模式:功能不变,只是换了数据格式
  • 组合模式:管理组合对象和叶子对象,为它们提供一致的操作给客户端
  • 策略模式:只有一层选择,选择某个功能

实现代码

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

面向切面编程(AOP):是对 OOP 的补充,在各个节点前后插入动作。

与装饰者模式区别: 装饰者模式是一种设计模式,而AOP是一种编程范式,可以看作是装饰者模式的实现。

与OOP区别:

  • OOP:针对业务处理过程的实体及其属性和行为进行抽象封装
  • AOP:针对业务处理过程中的某个步骤或阶段进行提取

例如:雇员抽象为Employee类(OOP),雇员上班(是一个AOP节点,上班前坐车和下班后吃饭是AOP的提取工作)

ES3下装饰者的实现

// 原函数
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()

ES5下装饰者的实现

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();

ES7修饰器实现装饰者

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装饰者模式