程序员简友广场想法

JS设计模式之装饰者模式

2020-09-14  本文已影响0人  Splendid飞羽

定义:

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。

应用场景

装饰者模式由于松耦合,多用于一开始不确定对象的功能、或者对象功能经常变动的时候。
尤其是在参数检查、参数拦截等场景。

1、简单的装饰器模式案例**

// 交通工具vehicle构造函数
function vehicle(vehicleType){
    // 默认值
    this.vehicleType = vehicleType || "car";
    this.model = "default";
    this.license = "00000-00000";
}

// 测试构造函数
var testInstance = new vehicle("car");
console.log(testInstance);

// 创建一个实例进行装饰
var truck = new vehicle("truck");

// 然后给其装饰上新的功能
truck.setModel = function(modelName){
    this.model = modelName;
}

truck.setColor = function(color){
    this.color = color;
}

默认的vehicle构造函数是没有setModel和setColor等方法的,通过重新创建实例,往实例中添加方法达到不修改原有的构造函数,同时 又能增加一些自定义的方法来进行装饰

2、ES6语法改写装饰器模式
class Shape {
  constructor(name) {
    this.name = name
  }

  draw() {
    console.log(`draw ${this.name}`)
  }
}

class ColorDecorator {
  constructor(shape) {
    this.shape = shape
  }

  draw() {
    this.shape.draw()
    this.setColor()
  }

  setColor() {
    console.log(`color the ${this.shape.name}`)
  }
}

let circle = new Shape('circle')
circle.draw()

let decorator = new ColorDecorator(circle)
decorator.draw()

其实增加colorDecrator是为了增加额外的方法比如设画完形状之后设置颜色,通过新增一个ColorDecorator 类,同时不改变原有的Shape类,这样我们可以通过调用装饰类colorDecorator来实现画画,然后设置颜色,如果Shape类经过多人引用,功能无法修改的情况下,通过这种方法能够很灵活的为Shape类增加自己想要的方法,这样也不会干扰到其他人对Shape类的使用。

3、装饰器模式的升级

const isFn = (fn) => typeof fn === "function";

const addDecorator = (fn, before, after) => {
    if (!isFn(fn)) {
        return () => {};
    }

    return (...args) => {
        let result;
        // 按照顺序执行“装饰函数”
        isFn(before) && before(...args);
        // 保存返回函数结果
        isFn(fn) && (result = fn(...args));
        isFn(after) && after(...args);
        // 最后返回结果
        return result;
    };
};

/******************以下是测试代码******************/

const beforeHello = (...args) => {
    console.log(`Before Hello, args are ${args}`);
};

const hello = (name = "user") => {
    console.log(`Hello, ${name}`);
    return name;
};

const afterHello = (...args) => {
    console.log(`After Hello, args are ${args}`);
};

const wrappedHello = addDecorator(hello, beforeHello, afterHello);

let result = wrappedHello("godbmw.com");
console.log(result);

4、JS实现AOP面向切面编程

封装before函数
功能:在需要执行的函数之前执行某个新添加的功能函数

//是新添加的函数在旧函数之前执行
Function.prototype.before=function (beforefn) {
    var _this= this;                               //保存旧函数的引用
    return function () {                           //返回包含旧函数和新函数的“代理”函数
        beforefn.apply(this,arguments);            //执行新函数,且保证this不被劫持,新函数接受的参数
                                                   // 也会被原封不动的传入旧函数,新函数在旧函数之前执行
        return _this.apply(this,arguments);
    };
};

封装 after 函数
功能:在需要执行的函数之后执行某个新添加的功能函数

//新添加的函数在旧函数之后执行
Function.prototype.after=function (afterfn) {
    var _this=this;
    return function () {
        var ret=_this.apply(this,arguments);
        afterfn.apply(this,arguments);
        return ret;
    };
};

来看实际调用

var func = function() {
    // 主要业务逻辑
    console.log("2")
}

// 在主要业务逻辑 实现之前添加一些功能
func.before(function() {
    console.log("1");
})()

// 在主要业务逻辑之前和之后添加一功能
func = func.before(function() {
    console.log("1");
}).after(function() {
    console.log("3");
})

func();

/*
func的值为
    function() {
        var ret = _self.apply(this, arguments);
        afterfn.apply(this, arguments);
        return ret;
    }
*/
// 调用func的时候_self为调用的主体,即before中return的function
/*
    function() { //返回包含了原函数和新函数的"代理函数"
        beforefn.apply(this, arguments); //执行新函数,修正this
        return _self.apply(this, arguments); //执行原函数
    }
*/
上面的AOP切面编程用到了高阶函数,可以参考我的另外一篇文章

JavaScript高阶函数

上一篇 下一篇

猜你喜欢

热点阅读