美文网首页程序员简友广场想法
JS设计模式之装饰者模式

JS设计模式之装饰者模式

作者: Splendid飞羽 | 来源:发表于2020-09-14 21:31 被阅读0次

    定义:

    装饰器模式(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高阶函数

    相关文章

      网友评论

        本文标题:JS设计模式之装饰者模式

        本文链接:https://www.haomeiwen.com/subject/rmtoektx.html