浅谈设计模式-JS

前阵子看了《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);
    
    

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

代理模式

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

浅谈设计模式-JS_第1张图片

比如我们上面单例模式中的代理类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
    
    
    

    
    
    
// 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;
    }
  }

中介者模式

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

浅谈设计模式-JS_第2张图片

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

你可能感兴趣的:(javascript,设计模式,前端)