浅谈对装饰者模式的理解?应用场景?

674 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

浅谈对装饰者模式的理解?应用场景?

image.png

一、是什么

装饰者模式(Decorator Pattern)就是动态的给类或对象增加职责的设计模式。它能在不改变类或对象自身的基础上,在程序的运行期间动态的添加职责,跟继承相比,装饰者是一种更轻便灵活的做法

这种设计模式非常符合敏捷开发的设计思想:先提炼出产品的最小可用产品,再通过快速迭代的方式添加功能

在生活中,同一张图片,组合不同的滤镜就会有不同的体验

这里实际上就应用了装饰者模式:是通过滤镜装饰了照片。在不改变对象(照片)的情况下动态的为其添加功能(滤镜)

二、使用

es6上,增添了类的装饰器,用来注释或修改类和类方法,使用类的装饰器实际上就应用了装饰者模式(关于类装饰器的使用这里不再陈述)如下:

image.png

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

要想为函数添加一些功能,最简单粗暴的方式就是直接改写该函数,但这是最差的办法,直接违反了开放-封闭原则,如下:

var a = function () {
  alert(1);
}
// 改成
var a = function () {
  alert(1);
  alert(2);
}

实际开发中,比如我们想给 window 绑定 onload 事件,但是又不确定这个事件是不是已经被其他人绑定过,为了避免覆盖掉之前的 window.onload 函数中的行为,我们一般都会先保存好原先的 window.onload,把它放入新的 window.onload 里执行

window.onload = function () {
  alert(1);
}
var _onload = window.onload || function () { };
window.onload = function () {
  _onload();
  alert(2);
}

同样,我们还可以使用AOP 面向切面编程来装饰函数

什么是面向切面编程?举个例子,餐前洗手、饭后漱口,吃饭这个动作相当于切点,我们可以在这个切点前、后插入其它如洗手等动作

下面则使用AOP来修饰onLoad

首先定义Function.prototype.before 方法和 Function.prototype.after 方法,如下:

Function.prototype.before = function (beforefn) {
  var __self = this// 保存原函数的引用
  return function () { // 返回包含了原函数和新函数的"代理"函数
    beforefn.apply(thisarguments); // 执行新函数,且保证 this 不被劫持,新函数接受的参数也会被原封不动地传入原函数,新函数在原函数之前执行
    return __self.apply(thisarguments); // 执行原函数并返回原函数的执行结果,并且保证 this 不被劫持
  }
}
Function.prototype.after = function (afterfn) {
  var __self = this;
  return function () {
    var ret = __self.apply(thisarguments);
    afterfn.apply(thisarguments);
    return ret;
  }
};

Function.prototype.before 接受一个函数当作参数,这个函数即为新添加的函数,它装载了新添加的功能代码

它的工作是把请求分别转发给新添加的函数和原函数,且负责保证它们的执行顺序,让新添加的函数在原函数之前执行(前置装饰),这样就实现了动态装饰的效果。通过 Function.prototype.apply 来动态传入正确的 this,保证了函数在被装饰之后,this 不会被劫持

Function.prototype.after 的原理跟 Function.prototype.before 一模一样,唯一不同的地方在于让新添加的函数在原函数执行之后再执行

修改后代码如下:

window.onload = function () {
  alert(1);
}
window.onload = (window.onload || function () { }).after(function () {
  alert(2);
}).after(function () {
  alert(3);
}).after(function () {
  alert(4);
});

三、总结

应用场景:

适合对已拥有必备组件的对象进行扩展高阶组件或属性

优点:

  • 无需修改现有对象,也无需创建对象的子类,即可扩展对象的功能。
  • 可以多扩展功能进行动态添加或删除
  • 可以扩展不同的装饰者来解决扩展不同功能的问题

参考文献

谢谢支持

如果有什么不同的想法,可以在评论区留言,大家一起学习呀~ 最后如果这篇文章对您有帮助的话,请一件三连给作者一点点支持,谢谢啦!

以上便是本次分享的全部内容,希望对你有所帮助^_^