Js 设计模式

Js 设计模式

通过比对不同模式的使用,更好的理解。

创建类:
  单例模式
结构性:
  装饰模式
  代理模式
行为性:
  状态模式
  策略模式
  模板模式
  观察者模式

单例模式

代码:

// 单例模式
var someClass = {
  _singleton: null,
  getSingleton: function() {
    if (!this._singleton) {
      this._singleton = {
        // some code here
      }
    }
    return this._singleton
  }
}

var instance = someClass.getSingleton()

为了保证实例不被改写,可以关闭它的写入开关

Object.defineProperty(namespace, "singleton", { 
  writable: false, 
  configurable: false, 
  value: { 
  // some value
  } 
});
代理模式 vs 装饰模式

参考:http://www.cnblogs.com/webFrontDev/archive/2013/05/13/3075857.html

装饰者虚拟代理都要对其他对象进行包装,都要实现与被包装对象相同的接口,而且都要把方法调用传递给被包装对象。

区别:1. 装饰者会对被包装对象的功能进行修改或扩充,而代理只不过是控制对它的访问。除了有时可能会添加一些控制代码之外,代理并不会对传递给本体的方法调用进行修改。而装饰着就是为修改方法而生的。2. 另一个区别表现在被包装对象的创建方式上。在装饰者模式中,被包装对象的实例化过程是完全独立的。这个对象创建出来之后,你可以随意为其裹上一个或更多装饰者。而在代理模式中,被包装对象的实例化是代理的实例化过程的一部分。在某些类型的虚拟代理中,这种实例化受到严格控制,它必须在代理内部进行。此外,代理不会像装饰者那样互相包装,它们一次只使用一个。

代理模式

普通代理

var MyClass = function(){}
MyClass.prototype.add = function(){}
MyClass.prototype.remove = function(){}

// 普通代理
var targetProxy = function(){
  this.target = new MyClass()
}
targetProxy.prototype.add = function(){
  return this.target.add()
}
targetProxy.prototype.remove = function(){
  return this.target.remove()
}

虚拟代理

// 虚拟代理(virtual proxy) 虚拟代理用于控制对那种创建开销很大的本体的访问。其本体实例化是在使用时才进行
var targetVirtualProxy = function(){
  this.args = arguments
  this.target = null
}
targetVirtualProxy.prototype._init = function(){
  if(!this.target) this.target = new MyClass(this.args)
}
targetVirtualProxy.prototype.add = function(){
  this._init()
  return this.target.add()
}
targetVirtualProxy.prototype.remove = function(){
  this._init()
  return this.target.remove()
}

远程代理(远程代理是一种结构型模式,不同场景会对应不同的模式)

// 远程代理
var SimpleHandler = function(){}
SimpleHandler.prototype = {
  request: function(method, url, callback, postVars){
    var xhr = this.createXhrObject()
    xhr.onreadystatechange = function() {
      if(xhr.readyState !== 4) return
      ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ?
      callback.success(xhr.responseText) : callback.failure(xhr.status)
    }
    xhr.open(method, url, true)
    postVars = method === 'POST' ? postVars : null
    xhr.send(postVars)
  },
  createXhrObject: function(){
    //return new XMLHttpRequest()
  }
}

var StatsProxy = (function(){
  var xhrHandler = new SimpleHandler()
  var urls = {
    text: '/text',
    json: '/json'
  }

  function fetch(url, callback){
    var callback = {
      success: function(responseText){
        console.log(responseText)
        callback && callback()
      },
      failure: function(code){
        console.log(code)
      }
    }
    xhrHandler.request('GET', url, callback)
  }

  return {
    getText: function(callback){
      fetch(urls.text, callback)
    },
    getJson: function(callback){
      fetch(urls.json, callback)
    }
  }
})()
// 优化版
var WebserviceProxy = function () {
  this.xhrHandler = new SimpleHandler();
};
WebserviceProxy.prototype = {
  _xhrFailure: function (statusCode) {
      throw new Error('StatsProxy:Asynchronous request for stats failed.')
  },
  _fetchData: function (url, dataCallback, getVars) {
      var that = this
      var callback = {
          success: function (responseText) {
              var obj = eval('(' + responseText + ')')
              dataCallback(obj);
          },
          failure: that._xhrFailure
      }

      var getVarArray = []
      for (var varName in getVars) {
          getVarArray.push(varName + '=' + getVars[varName])
      }
      if (getVarArray.length > 0) {
          url = url + '?' + getVarArray.join('&')
      }

      this.xhrHandler.request('GET', url, callback)
  }
}

var StatsProxy = function(){ StatsProxy.superclass.constructor.call(this) }
function _extends(subClass, superClass){
  var F = function () {}
  F.prototype = superClass.prototype
  subClass.prototype = new F()
  subClass.prototype.constructor = subClass

  subClass.superclass = superClass.prototype
  if (superClass.prototype.constructor === Object.prototype.constructor) {
      superClass.prototype.constructor = superClass
  }
}
_extends(StatsProxy, WebserviceProxy)

StatsProxy.prototype.getText = function(callback){
  this._fetchData('/text', callback, {key: 'Jay'})
}

装饰模式

// 装饰模式
var Interface = function(){}
Interface.prototype.eat = function(){}
Interface.prototype.sleep = function(){}

var InterfaceDecorator = function(_interface){
  this._interface = _interface
}
InterfaceDecorator.prototype = {
  eat: function(){
    return this._interface.eat()
  },
  sleep: function(){
    return this._interface.sleep()
  },
  run: function(){
    return 'running.'
  }
}
// 使用
var HeadInterfaseDecorator = function(_interface){
  HeadInterfaseDecorator.superClass.constructor.call(this, _interface)
}
_extend(HeadInterfaseDecorator, InterfaceDecorator)
HeadInterfaseDecorator.prototype.eat = function(){
  return this._interface.eat() + ' and eat very much.'
}
状态模式 vs 策略模式

区别:在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;而策略模式里,采取何种策略由外部条件决定。

直接看如下代码示例,同样的功能,不同模式的实现:

状态模式

// 状态模式
!(function () {
    var LightEvent = {
        on:function(){
            console.log("关灯")
            this.state = "off"
        },
        off:function(){
            console.log("开灯")
            this.state = "on"
        }
    }
    var Light = function () {
        this.state = "off"
        this.button = null
    }
    Light.prototype.init = function(){
        var button = document.createElement("button"), self = this
        button.innerHTML = "开关"
        this.button = document.body.appendChild(button)
        this.button.onclick = function(){
            self.buttonWasPressed()
        }
    }
    Light.prototype.buttonWasPressed = function(){
        LightEvent[this.state].apply(this,arguments)
    }
    var light = new Light()
    light.init()
})()

策略模式

// 策略模式代码
!(function () {
    var delegate = function(client,delegation){
        return {
            buttonWasPressed:function(){
                return delegation.buttonWasPressed.apply(client,arguments)
            }
        }
    }
    var FSM = {
        off: {
            buttonWasPressed: function () {
                console.log("关灯")
                this.currHandler = this.onHandler
            }
        },
        on: {
            buttonWasPressed: function () {
                console.log("开灯")
                this.currHandler = this.offHandler
            }
        }
    }
    var Light = function () {
        this.offHandler = delegate(this,FSM.off)
        this.onHandler = delegate(this,FSM.on)
        this.currHandler = FSM.off
        this.button = null
    }
    Light.prototype.init = function () {
        var button = document.createElement("button"), self = this
        button.innerHTML = "开关"
        this.button = document.body.appendChild(button)
        this.button.onclick = function () {
            self.currHandler.buttonWasPressed.call(self)
        }
    }
    var light = new Light()
    light.init()
})()

示例中:仅看onclick 处理方法,
状态模式self.buttonWasPressed() 无论点击多少次,调用的都是实例(light 对象)本身的方法,每次调用内部状态自动变化;
策略模式self.currHandler.buttonWasPressed.call(self),每次调用buttonWasPressed方法时,不是直接调用实例(light 对象)的方法,而是调用指定处理者(即handler)的方法。

模板模式

优点:模板方法模式是一种实现代码复用的很好的手段。通过把子类的公共功能提炼和抽取,把公共部分放到模板中去实现。
缺点:子类、父类耦合高,要改动模板,通常子类要跟着改动。

// 模板模式
function AbstractClass() {
}

AbstractClass.prototype = {
  // 抽象的操作,必须有子类提供实现
  doPrimitiveOperation1: function(){},
  doPrimitiveOperation2: function(){},
  // 模板方法
  templateMethod: function(){
    this.doPrimitiveOperation1()
    this.doPrimitiveOperation2()
  }
}

function ConcreteClass() {
}

ConcreteClass.prototype = {
  __proto__: AbstractClass.prototype,
  doPrimitiveOperation1: function(){
    console.log('do operation1')
  },
  doPrimitiveOperation2: function(){
    console.log('do operation2')
  }
}

观察者模式

观察者模式有被称为发布订阅模式

定义:定义对象间的一种一对多的依赖关系
本质:触发联动
命名建议:目标接口的定义,建议在名称后面跟Subject;观察者接口的定义,建议在后面跟Observer;观察者接口的更新方法,建议名称为update,当然方法的参数可以根据需要定义,参数个数不限,参数类型不限。
优点:观察者模式实现了动态联动;观察者模式支持广播通信
缺点:可能会引起无谓的操作(由于是广播,一些地方不需要update 也调用了)

// 示例代码

/**
 * 目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口
 */
var Subject = function(){
  this.observers = []
}
Subject.prototype = {
  attach: function(observer){
    this.observers.push(observer)
  },
  detach: function(observer){
    for(var i=0; i<this.observers.length; i++){
      if(observer == this.observers[i]){
        this.observers.splice(i, 1)
        return true
      }
    }
    return false
  },
  notifyObservers: function(){
    for(var i=0; i<this.observers.length; i++){
      this.observers[i].update(this)
    }
  }
}

/**
 * 具体的目标对象,负责把有关状态存入到对应的观察者对象
 * 自己状态发生变化时,通知各个观察者
 */
var ConcreteSubject = function(){
  ConcreteSubject.super.constructor.call(this)
  // 目标对象状态
  this.subjectState = ''
}
_extends(ConcreteSubject, Subject)
ConcreteSubject.prototype.getSubjectState = function(){
  return this.subjectState
}
ConcreteSubject.prototype.setSubjectState = function(subjectState){
  this.subjectState = subjectState
  this.notifyObservers()
}

/**
 * 观察者
 */
var Observer = function(){
}
Observer.prototype.update = function(){}

var ConcreteObserver = function(){
  this.observerState = ''
}
_extends(ConcreteObserver, Observer)
ConcreteObserver.prototype.update = function(subject){
  this.observerState = subject.getSubjectState()
}

资料地址:
javaScript 标准参考教程
js 设计模式系列

(未完,待续~)

展开阅读全文

没有更多推荐了,返回首页