面向对象增加功能的问题
在我们传统的面向对象开发中,给对象添加功能时,我们往往会采用继承的方式,继承虽然能够解决问题,但是随之也带来一些问题:
- 父类和子类存在强耦合的关系,当父类改变时,子类也会跟着改变;
- 子类需要知道父类中的细节,从而进行复用或重写,这样其实破坏了封装性;
- 继承的方式,可能会创建出大量子类。
装饰器的目的就是在不改变原有的类的基础上,为其在运行期间动态的添加职责。
装饰器模式
在不改变自身对象的基础上,可以给对象附加更多的功能;装饰模式能够在不使用创造更多子类的状况下,将对象的功能加以扩展。
//原有对象
function show() {
console.log("原有功能")
}
//装饰器1
function logger(show) {
return function(msg) {
const result = show.apply(this, arguments);
console.log(msg)
return result
}
}
// 装饰器2
function fn2(show) {
return function() {
const result = show.apply(this, arguments);
console.log('功能2')
return result
}
}
let log = new logger(new fn2(show))
log('log*****')
// 原有功能
// 功能2
// log*****
利用aop的思想,增加before/after两个装饰方法
Function.prototype.before = function (beforeFn) {
const _self = this;
return function () {
beforeFn.apply(this, arguments); // 增加的功能
return _self.apply(this, arguments); // 原有功能
}
}
Function.prototype.after = function (afterFn) {
const _self = this;
return function () {
const result = _self.apply(this, arguments); // 原有功能
afterFn.apply(this, arguments);
return result;
}
}
let func = function () {
console.log('原有功能');
}
func = func.after(function() {
console.log('新增功能1');
}).after(function() {
console.log('新增功能2');
})
func();
// 原有功能
// 新增功能1
// 新增功能2
优点
- 可以动态的给原对象添加功能,'即用即付',非常灵活。
- 添加新功能的同时不会修改原对象,符合开闭原则。
- 装饰对象与原对象松耦合,易于维护。
缺点
- 定义过多的装饰类,会增加系统的复杂性,增加调试的难度。
应用场景:
vue数组变异方法、react高阶组件、原生数组filter、map、reduce方法等等
vue数组变异方法
在保留原生数组方法的基础上,增加响应式功能
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args) //原生数组方法
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})