美文网首页
js实现各种设计模式 最全最详细

js实现各种设计模式 最全最详细

作者: thunderQin | 来源:发表于2019-12-27 11:24 被阅读0次

    简单工厂模式

    1. 定义: 又叫静态工厂方法,根据不同参数创建不同对象,并赋予属性和方法
    2. 应用:抽取类相同的属性和方法封装到对象
    3. 实例
     let UserFactory = function (role) {
      function User(opt) {
        // 公共的属性和方法提取出来
        this.name = opt.name;
        this.viewPage = opt.viewPage;
      }
      // 根据不同参数返回不同参数
      switch (role) {
        case 'superAdmin':
          return new User(superAdmin);
          break;
        case 'admin':
          return new User(admin);
          break;
        case 'user':
          return new User(user);
          break;
        default:
          throw new Error('参数错误, 可选参数:superAdmin、admin、user')
      }
    }
    
    //调用
    let superAdmin = UserFactory('superAdmin');
    let admin = UserFactory('admin') 
    let normalUser = UserFactory('user')
    //最后得到角色,可以调用
    

    工厂方法模式

    1. 定义: 对产品类的抽象使其创建业务主要负责用于创建多类产品的实例
    2. 应用: 创建实例
    3. 代码
    function createPerson(name, age) {
        var obj = {};
        obj.name = name;
        obj.age = age;
        obj.writeJs = function () {
            console.log(this.name + 'write js');
        }
        return obj;
    }
    
    var p1 = createPerson('mengzhe' , 26);
    p1.writeJs();
    
    var p2 = createPerson('iceman' , 25);
    p2.writeJs();
    
    

    抽象工厂模式和工厂模式的区别就是: 前者是通过参数返回不一样的实例,后者是直接new出来相应的实例

    单例模式

    1. 定义: 一个实例只能被创建一次
    2. 应用: 提供命名空间
    3. 代码
    let singleCase = function(name){
        this.name = name;
    };
    singleCase.prototype.getName = function(){
        return this.name;
    }
    // 获取实例对象
    let getInstance = (function() {
        var instance = null;
        return function(name) {
            if(!instance) {//相当于一个一次性阀门,只能实例化一次
                instance = new singleCase(name);
            }
            return instance;
        }
    })();
    // 测试单体模式的实例,所以one===two
    let one = getInstance("one");
    let two = getInstance("two");   
    

    外观模式

    1. 定义: 为子系统中的一组接口提供一个一致的界面

    1. 应用: 简化复杂接口( stopPrevent )
    2. 比方:比如在家要看电影,需要打开音响,再打开投影仪,再打开播放器等等,引入外观角色之后,只需要调用“打开电影设备”方法就可以。外观角色封装了打开投影仪等操作,给使用者提供更容易使用的方法。
    3. 代码
    function a(x){
       // do something
    }
    function b(y){
       // do something
    }
    function ab( x, y ){
        a(x);
        b(y);
    } 
    

    适配器模式

    1. 定义:将一个接口转换成客户端需要的接口而不需要去修改客户端代码,使得不兼容的代码可以一起工作
    2. 应用:适配函数参数
    3. 代码
     // 参数适配模拟eg1:
    function add (x1, x2, x3) {
      console.log(x1 + x2 + x3);
    }
    // 存在一个对象数据
    var obj = {
      a: '我',
      b: '很',
      c: '帅'
    }
    function adapter (o) {
      // 通过适配器函数来调用目的api
      add(o.a, o.b, o.c);
    } 
    adapter(obj);
    

    装饰者模式

    1. 定义:不改变原对象的基础上,给对象添加属性或方法
    2. 应用:@Form @connect 给类关联上对应的属性
    3. 代码
     //获取事件源
      let input=document.getElementById(input);
      //若事件源已经绑定事件
      if(typeof input.onclick=='function'){
        //缓存事件源原有的回调函数
        let oldClickFn=input.onclick;
        //为事件源定义新事件
        input.onclick=function(){
          //事件源原有回调函数
          oldClickFn();
          //执行事件源新增回调函数
          fn();
        }
      }else{
        //未绑定绑定
        input.onclick=fn;
      }
    }
    
    //测试用例
    decorator('textInp',function(){
      console.log('文本框执行啦');
    })
    decorator('btn',function(){
      console.log('按钮执行啦');
    })
    

    观察者模式

    1. 作用: 解决类与对象,对象与对象之间的耦合,两个组件之间的通信
    2. 场景: VUE的bus,onclick事件绑定
    3. 代码
    let Observer=
      (function(){
        let _message={};
        return {
          //注册接口,
            //1.作用:将订阅者注册的消息推入到消息队列
            //2.参数:所以要传两个参数,消息类型和处理动作,
            //3.消息不存在重新创建,存在将消息推入到执行方法
            
          regist:function(type,fn){
            //如果消息不存在,创建
            if(typeof _message[type]==='undefined'){
              _message[type]=[fn];
            }else{
              //将消息推入到消息的执行动作
              _message[type].push(fn);
            }
          },
    
          //发布信息接口
            //1.作用:观察这发布消息将所有订阅的消息一次执行
            //2.参数:消息类型和动作执行传递参数
            //3.消息类型参数必须校验
          fire:function(type,args){
            //如果消息没有注册,则返回
            if(!_message[type]) return;
              //定义消息信息
              var events={
                type:type, //消息类型
                args:args||{} //消息携带数据
              },
              i=0,
              len=_message[type].length;
              //遍历消息
              for(;i<len;i++){
                //依次执行注册消息
                _message[type][i].call(this,events);
              }
          },
    
          //移除信息接口
            //1.作用:将订阅者注销消息从消息队列清除
            //2.参数:消息类型和执行的动作
            //3.消息参数校验
          remove:function(type,fn){
            //如果消息动作队列存在
            if(_message[type] instanceof Array){
              //从最后一个消息动作序遍历
              var i=_message[type].length-1;
              for(;i>=0;i--){
                //如果存在该动作在消息队列中移除
                _message[type][i]===fn&&_message[type].splice(i,1);
              }
            }
          }
        }
      })()
    
    //测试用例
      //1.订阅消息
      Observer.regist('test',function(e){
        console.log(e.type,e.args.msg);
      })
    
      //2.发布消息
      Observer.fire('test',{msg:'传递参数1'});
      Observer.fire('test',{msg:'传递参数2'});
      Observer.fire('test',{msg:'传递参数3'});
    

    中介者模式

    1. 定义:设置一个中间层,处理对象之间的交互
    2. 代码:
    var mediator = (function() {
        var topics = {},
            subUid = -1;
        var publish = function(topic, args) {
            if (!topics[topic]) {
                return false;
            }
    
            var subscribers = topics[topic],
                len = subscribers ? subscribers.length : 0;
            while (len--) {
                subscribers[len].func(topic, args);
            }
    
            return true;
        };
    
        var subscribe = function(topic, func) {
            if (!topics[topic]) {
                topics[topic] = [];
            }
    
            var token = (++subUid).toString();
            topics[topic].push({
                token: token,
                func: func
            });
    
            return token;
        };
    
        return {
            publish: publish,
            subscribe: subscribe,
            installTo: function(obj) {
                obj.publish = publish;
                obj.subscribe = subscribe;
            }
        }
    }());
    
    
    // 具体应用
    var mod1 = {
        run: function(arg) {
            console.log('mod1 received ' + arg);
        }
    };
    var mod2 = {};
    var topic = 'myTopic';
    mediator.installTo(mod1);
    mediator.installTo(mod2);
    // mod1订阅消息
    mod1.subscribe(topic, function(t, arg) {
        mod1.run(arg);
    });
    // mod2发布消息
    mod2.publish(topic, 'data');
    

    中介者模式和观察者模式的区别就是

    1. 中介模式强调同事(colleague)之间的交互,需要知道所有同事的信息,相互影响(买房的时候,中介需要知道双方需求和信息)
    2. 观察者模式中,其实是发布和订阅模式,发布者不需要知道订阅的信息(收听广播,广播站只管发布,不需要接收订阅者信息)

    访问者模式

    1. 定义:通过继承封装一些该数据类型不具备的属性,
    2. 作用:主要将稳定的数据结构和易变的操作分开,方便扩展多变的操作方法而保持稳定的数据结构
    3. 代码
    // 定义奖金的访问者,在js中简单的用一个函数模拟,
    // 如果在c#等强类型语言中,需要声明一个接口,不同的visitor实现不同的计算奖金方法,
    // 比如下面的管理者和开发者奖金不一样
    function bonusVisitor(employee) {
      if (employee instanceof Manager)
        employee.bonus = employee.salary * 2;
      if (employee instanceof Developer)
        employee.bonus = employee.salary;
    }
    
    // 定义员工类,注意继承此类的必须都带有accept这个接受visitor的方法,
    // 就是用来接待访问者,进而内部用访问者调用自己方法实现一些操作,
    // 此例中直接调用函数visitor(this)
    class Employee {
    
      constructor(salary) {
        this.bonus = 0;
        this.salary = salary;
      }
    
      accept(visitor) {
        visitor(this);
      }
    }
    
    // 管理者实现员工类
    class Manager extends Employee {
      constructor(salary) {
        super(salary);
      }
    }
    
    // 开发者实现员工类
    class Developer extends Employee {
      constructor(salary) {
        super(salary);
      }
    }
    

    使用

    // 管理员工集合
        let employees = [];
    
        // 不同的员工
        const john = new Developer(4000);
        const christian = new Manager(10000);
    
       // 放入员工集合中
        employees.push(john);
        employees.push(christian);
    
         // 分别接收访问者并调用
        employees.forEach(e => {
          e.accept(bonusVisitor);
        });
    

    员工数据是稳定的,但是加薪的操作是不确定的,访问模式将两者分开。

    状态模式

    1. 定义:一个对象状态改变会导致行为变化
    2. 作用:解决复杂的if判断
    3. 代码
    var State = function(){
        var States = {
            state0 : function(params){
                //your coding...
                console.log('state0~');
            },
            state1 : function(params){
                //your coding...
                console.log('state1~');
            },
            state2 : function(params){
                //your coding...
                console.log('state2~');
            },
            state3 : function(params){
                //your coding...
                console.log('state3~');
            },
        };
        function getResult(result){
            States['state'+result] && States['state'+result]();
        }
        return{
            setState : getResult
        } 
    }();
    
    State.setState(0);
    

    注解:将if else转化为 map的过程 的过程

    策略模式

    1. 定义了一系列家族算法,并对每一种算法单独封装起来,让算法之间可以相互替换,独立于使用算法的客户
    2. 作用: 把算法从业务中单独抽离出来
    3. 代码
    var tween = {
         linear: function(t, b, c, d){
             return c*t/d + b;
         },
         easeIn: function(t, b, c, d){
             return c * ( t /= d ) * t + b;
         },
         strongEaseIn: function(t, b, c, d){
             return c * ( t /= d ) * t * t * t * t + b;
         },
         strongEaseOut: function(t, b, c, d){
            return c * ( ( t = t / d -1 ) * t * t * t * t + 1 ) + b;
         },
         sineaseIn: function(t, b, c, d){
             return c * ( t /= d ) * t * t + b;
         },
         sineaseOut: function(t, b, c, d){
            return c * ( ( t = t / d -1 ) * t * t + 1 ) +b;
         }
     };
    
    
    /*
     * 负责启动运动动画
     */
    Animate.prototype.start = function( propertyName, endPos, duration, easing ){
        this.startTime = +new Date; // 启动动画的时间
        this.startPos = this.dom.getBoundingClientRect()[ propertyName ]; // dom 节点的初始位置
        this.propertyName = propertyName; // dom 节点需要被改变的 CSS 属性名
        this.endPos = endPos; // dom 节点的目标位置
        this.duration = duration; // 动画的持续时间
        this.easing = tween[ easing ]; // 缓动算法
    
        // 启动动画定时器
        var self = this;
        var timeId = setInterval(function(){
            if( self.step() === false){
                clearInterval(timeId);
            }
        },20);
    };
    

    桥接模式

    1. 定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化
    2. 代码
       //bad
        addEvent(Element,'click',getBeerById)
        function getBeerById(e){
          // 事件对象被作为参数传递给函数,而本例并没有使用这个参数,只是从this对象中获取id
          var id = this.id;
          asyncRequest('GET',`beer.uri?id=${id}`,function (res) {
             console.log(`Request Beer:${res.responseText}`)  
          }) 
        }
        // good
        function getBeerById(id,callback) {
          asyncRequest('GET',`beer.uri?id=${id}`,function (res) {
            // 回调 传入返回值
            callback(res.responseText)
          }) 
        }
        addEvent(Element,'click',getBeerByIdBridge)
        function getBeerByIdBridge(e){
          // 把id作为参数传递给getBeerById函数是合情合理的,这里使用一个回调函数把回应结果返回 现在我们将针对接口而不是实现进行编程 
          getBeerById(this.id,function (beer) {
            console.log(`Request Beer:${res.responseText}`)
          })
        }
    

    我们用了两种方式来实现,第一种方式把事件对象与getBeerById捆绑在一起,那么它只在本次事件中适用。第二种方式使用桥接模式把抽象隔离开来后,getBeerById 函数不再和事件对象捆绑在一起,也就扩大了它的适用范围

    参照链接

    1. [https://www.jianshu.com/p/f362d67ed659]
    2. [https://www.jianshu.com/p/7e582eec6b84]
    3. [https://www.cnblogs.com/Medeor/p/5001841.html]

    相关文章

      网友评论

          本文标题:js实现各种设计模式 最全最详细

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