设计模式-javascript实现【装饰者模式】
定义:给对象动态地增加职责的方式称为装饰者模式,装饰者模式能够在不改变对象自身的基础上,在程序运行
期间给对象动态地添加职责。
从功能上而言,装饰器能很好地描述这个模式,但从结构上看,包装器的说法更加贴切。装饰者模式将一个对象
嵌入另一个对象之中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链。请求随着这条链条依次
传递到所有的对象,每个对象都有处理这条请求的机会。
1. 模拟传统面向对象的装饰者模式
class Plane {
fire() {
console.log('fire the bullet');
}
}
class Missile {
constructor(plane) {
this.plane = plane;
}
fire () {
this.plane.fire();
console.log('fire the Missile');
}
}
class Atom {
constructor(plane) {
this.plane = plane;
}
fire() {
this.plane.fire();
console.log('fire the Atom');
}
}
let plane = new Plane();
plane = new Missile(plane);
plane = new Atom(plane);
// 依次输出:fire the bullet, fire the Missile, fire the Atom
plane.fire();
2. 使用重写对象方法的方式实现装饰者模式
const plane = {
fire: () => {
console.log('fire the bullet');
}
};
const fireMissile = () => {
console.log('fire the Missile');
};
const fireAtom = () => {
console.log('fire the Atom');
};
const fireBullet = plane.fire;
plane.fire = () => {
fireBullet();
fireMissile();
};
const fireBulletAndMissile = plane.fire;
plane.fire = () => {
fireBulletAndMissile();
fireAtom();
};
plane.fire();
3. 使用AOP装饰函数
- 通过在函数原型上增加装饰方法
// 前置装饰
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 res = self.apply(this, arguments);
afterfn.apply(this, arguments);
return res;
};
}
// 测试装饰函数
function test(){
console.log(0);
}
function test1(){
console.log(1);
}
function test2() {
console.log(2);
}
test.before(test1).before(test2)(); // 2, 1, 0
test.after(test1).after(test2)(); // 2, 1, 0
- 通过高阶函数的方式实现装饰模式
// fn为原函数,beforefn为装饰函数, 生成的装饰后的函数其内部返回原函数执行后的值
// 前置装饰
const before = (fn, beforefn) => (...args) => {
beforefn(...args);
return fn(...args);
}
// 后置装饰
const after = (fn, afterfn) => (...args) => {
const res = fn(...args);
afterfn(...args);
return res;
}
const test = () => console.log(0);
const test1 = () => console.log(1);
const test2 = () => console.log(2);
before(before(test, test1), test2)();
after(after(test, test1), test2)();