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 设计模式系列
(未完,待续~)