美文网首页
浅谈设计模式-JS

浅谈设计模式-JS

作者: 唐_银 | 来源:发表于2020-09-04 16:37 被阅读0次

    前阵子看了《JavaScript设计模式与开发实践》,非常不错的一本书,整理一些最常用的设计模式,以后再补充一些其它的。

    单例模式

    保证一个类仅有一个实例,并且提供一个访问它的全局访问点。

    实现单例模式

    1. 利用构造函数的实例属性(不透明)

      let CreateDiv = function (name) {
          this.name = name
          this.instance = null
      }
      CreateDiv.prototype.init = function () {
          let div = document.createElement( 'div' )
          // ....
      }
      CreateDiv.creatInstance = function (name) {
          if (!this.instance) {
              this.instance = new CreateDiv(name)
          }
          return this.instance
      }
      let lee1 = CreateDiv.creatInstance('lee1')
      let lee2 = CreateDiv.creatInstance('lee2')
      
      lee1 === lee2   // true
      
    2. 利用闭包 (不透明)

      let CreateDiv = function (name) {
          this.name = name
      }
      CreateDiv.prototype.init = function () {
          let div = document.createElement( 'div' )
          // ....
      }
      CreateDiv.creatInstance = (function () { // 立即执行函数
          let instance = null
          return function (name) {
              if (!instance) {
                  instance = new CreateDiv(name)
              }
              return instance
          }
      })()
      let lee1 = CreateDiv.creatInstance('lee1')
      let lee2 = CreateDiv.creatInstance('lee2')
      
      lee1 === lee2   // true
      
    3. 单例类(透明)

      上面两种实现方式不透明,需要研究代码的实现才知道调creatInstance方法,这样不太好啊,我门用普通类来实现

      let Singleton = (function () {
          let instance = null
          let CreateDiv = function (name) {
              if (instance) return instance
              this.name = name
              this.init() // 创建一个div
              return instance = this // this指向新创建的实例对象
          }
          CreateDiv.prototype.init = function () {
              let div = document.createElement( 'div' )
              // ....
          }
          return CreateDiv
      })()
      let lee1 = new Singleton('lee1')
      let lee2 = new Singleton('lee2')
      
      lee1 === lee2   // true
      
    4. 代理类(代理模式 - 透明)

      // 有一我们要创建100个div怎么办 恐怕只能修改CreateDiv了吧
      // 代理类只负责单例模式,不影响原来的类,而且也很透明
      let CreateDiv = function (name) {
          this.name = name
          this.init()
      }
      CreateDiv.prototype.init = function () {
          let div = document.createElement( 'div' )
          // ....
      }
      <!--代理类 负责单例模式 不影响-->
      let ProxyCreatInstance = (function () {
          let instance = null
          return function (name) {
              if (!instance) {
                  instance = new CreateDiv(name)
              }
              return instance   // 返回了实例
          }
      })()
      let lee1 = new ProxyCreatInstance('lee1') // 直接new 很透明,创建多个还用CreateDiv类
      let lee2 = new ProxyCreatInstance('lee2')
      
      lee1 === lee2   // true
      // 这里涉及到了new操作符原理,构造函数如果返回了引用类型,会覆盖掉产生的对象
      function mockNew (func, params) {
        let obj = {}
        obj.__proto__ = func.prototype
        let res = func.call(obj, params)
        return res instanceof Object ? res : obj
      }
      
    5. js惰性单例(上面的都混入了传统面向对象语言的特点,没有利用js的特点)

      // js嘛 函数是一等公民, 先写个通用的单例函数
      function getSingle (fn) {
          let instance = null
          return function () {
              return instance || (instance = fn.call(this, arguments)) // fn有返回 这样写更简洁
          }
      }
      let CreateDiv = function (name) {
          this.name = name
          let div = document.createElement( 'div' )
          div.setAttribute('id', name[0])
          return div
      }
      
      let singleton = getSingle(CreateDiv)
      
      let lee1 = singleton('lee1')
      let lee2 = singleton('lee2')
      
      lee1 === lee2   // true
      
      button.addEventListener('click', function () {
          let lee = singleton('lee')
      } , false);
      <!--惰性单例就是指只在需要的时候才创建 比如我点击按钮后创建-->
      
      

    小结:不透明模式需要了解代码,不够友好,基于传统的面向对象,我们可以通过代理类来实现单例模式的透明化,创建对象和管理单例的职责被分布在两个不同的方法中。惰性单例技术就是指在合适的时候才创建对象。

    代理模式

    代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。当我们不方便直接访问对象本体的时候,通过代理去访问它。

    image

    比如我们上面单例模式中的代理类ProxyCreatInstance,我们如果不用代理,就像第三种单例类一样,代码耦合在一起,假如我们想创建100个div,只能去更改CreateDiv的内部,因为你new 100单例模式下个它也只有一个。用了代理模式后,当你需要用单例的时候走代理类就ok了,不影响原来的类。

    小王要给小芳送花,他不善言辞,送花失败率高,他让小芳的朋友小明替他去送,小明挑了一个小芳开心的日子去送花,一下车替小王解决了终身大事。。。小明这个代理可以监听小芳的心情,在她心情好的时候再去送,这就是代理模式的作用。

    function Flower () {}
    
    let xiaoWang = {
        sendFlower: function (target) {
            let flower = new Flower()
            target.receiveFlower(flower)
        }
    }
    let xiaoMing =  {
        receiveFlower: function (flower) {
            xiaoFang.listenGoodMood(function () {
                xiaoFang.receiveFlower(flower)   
            }
        })
    }
    let xiaoFang =  {
        receiveFlower: function (flower) {
            console.log('收到花')
        },
        listenGoodMood: function (fn) {
            setTimeout(fn, 10000)
        }
    }
    
    xiaoWang.sendFlower(xiaoMing)
    

    策略模式

    定义一系列的算法,将不变的部分和变化的部分隔开,实际就是将算法的使用和实现分离出来,算法的使用方式是不变的

    举个栗子

    <!--年终核算绩效-->
    A级 奖金为工资*3
    B级 奖金为工资*2
    C级 奖金为工资*1
    D级 奖金为0
    
    function calculateBonus ( performanceLevel, salary ){
        if ( performanceLevel === 'A' ){
            return salary * 3;
        }
        if ( performanceLevel === 'B' ){
            return salary * 2;
        }
        if ( performanceLevel === 'C' ){
            return salary * 1;
        }
    };
    calculateBonus( 'A', 10000 );
    calculateBonus( 'B', 5000 );
    
    ---------------------------------------------
    <!--这是我们的策略-->
    const strategies = {
        'A': function (salary) {
            return salary * 3;
        },
        'B': function (salary) {
            return salary * 2
        },
        'C': function (salary) {
            return salary * 1
        },
        'D': function (salary) {
            return salary * 0
        }
    }
    <!--使用方法是不变的-->
    const calculateBonus = function (performanceLevel, salary) {
        return strategies[performanceLevel](salary)
    }
    
    calculateBonus( 'A', 10000 );
    calculateBonus( 'B', 5000 );
    

    发布订阅模式

    它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。例如你订阅了一些情报,情报中心刚才发布了一些新消息,如果里面有你订阅的情报时,就会通知到你。发布订阅的好处是发布和订阅是完全解耦的,它们通过消息类型关联。

    像js中的addEventListener、vue中的bus通信都是用的发布订阅模式。

    button.addEventListener("click", function () {}, false);
    
    // html
        <button onclick="handleOn('m1')">订阅-放学啦</button>
        <button onclick="handleOn('m2')">订阅-老师来了</button>
        <button onclick="handleOn('m3')">订阅-打一把王者</button>
    
        <button onclick="handleSub('m1')">发布-放学啦</button>
        <button onclick="handleSub('m2')">发布-老师来了</button>
        <button onclick="handleSub('m3')">发布-打一把王者</button>
    // js
      /**
       * 发布订阅模式 发布订阅互不影响,通过消息类型关联
       */
      var Observe = function () {
        // 定义消息队列
        this._msgQueue = {}
      }
      Observe.prototype = {
        // 消息订阅
        subscribe: function (type, fn, msg) {
          if (this._msgQueue[type] === void (0)) {
            this._msgQueue[type] = [fn]
          } else {
            this._msgQueue[type].push(fn)
          }
          alert(msg)
        },
        // 发布
        publish: function (type, args) {
          console.log(this._msgQueue[type])
          if (!this._msgQueue[type]) return
          let params = {
            type: type,
            args: args || {}
          }
          let i = 0, len = this._msgQueue[type].length;
          for (; i < len; i++) {
            this._msgQueue[type][i].call(this, params)
          }
        },
        // 移除消息订阅
        off: function (type, fn) {
          if (this._msgQueue[type] instanceof Array) {
            let i = 0, len = this._msgQueue[type].length;
            for (; i < len; i++) {
              if (this._msgQueue[type][i] === fn) {
                this._msgQueue[type].splice(i, 1)
                return
              }
            }
          }
        }
      }
      // -----------------------------------
      var Ming = new Observe()
      function handleOn(type) {
        console.log('type', type);
        switch (type) {
          case 'm1':
            console.log('m1');
            let fun1 = function (params) {
              alert(params.args)
            }
            Ming.subscribe(type, fun1, '放学了-订阅成功')
            break;
          case 'm2':
            let fun2 = function (params) {
              alert(params.args)
            }
            Ming.subscribe(type, fun2, '老师来了-订阅成功')
            break;
          case 'm3':
            let fun3 = function (params) {
              alert(params.args)
            }
            Ming.subscribe(type, fun3, '打一把王者-订阅成功')
            break;
        }
      }
      function handleSub(type) {
        switch (type) {
          case 'm1':
            Ming.publish(type, '放学了,快回家找妈妈')
            break;
          case 'm2':
            let fun2 = function (params) {
              alert(params.args)
            }
            Ming.publish(type, '老师来了-快把手机藏起来')
            break;
          case 'm3':
            let fun3 = function (params) {
              alert(params.args)
            }
            Ming.publish(type, '同学们请注意,这堂课我们一起玩王者农药')
            break;
        }
      }
    
    

    中介者模式

    中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可

    image

    缺点:系统中会新增一个中介者对象,增加了对象之间交互的复杂性,中介者对象经常是巨大的,有时候难以维护。

    相关文章

      网友评论

          本文标题:浅谈设计模式-JS

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