第1页
小谈Javascript设计模式
鲁超伍|Adam
http://adamlu.com/
http://twitter.com/adamlu
第2页
为什么要使用设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
第3页
设计模式(Design Patterns)
工厂(Factory)模式
单体(Singleton)模式
装饰者(Decorator)模式
桥接(Bridge)模式
适配器(Adapter)模式
观察者(Observer)模式
门面(Façade)模式
策略(Strategy)模式
命令(Command)模式
职责链(Chain Of Responsibility)模式
组合(Composite)模式
享元(Flyweight)模式
……
第4页
工厂(Factory)模式
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
假如你不知道在运行时哪些类会被创建,在Javascript中这是因为浏览器的不同,如果是IE,你需要创建一个实例,如果是其它浏览器你需要创建另一个实例。关键是返回的对象有同样的属性和方法。
第5页
Factory-EXP
if (typeof XMLHttpRequest != "undefined") {return new XMLHttpRequest();} else if (typeof window.ActiveXObject != "undefined") {return new ActiveXObject("MSXML2.XMLHttp");}
function XMLHttpFactory() {}XMLHttpFactory.createXMLHttp = function () {if (typeof XMLHttpRequest != "undefined") {return new XMLHttpRequest();} else if (typeof window.ActiveXObject != "undefined") {return new ActiveXObject("MSXML2.XMLHttp");}}
var xmlhttp=XMLHttpFactory.createXMLHttp();
第6页
单体(Singleton)模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
可以来划分命名空间,从而清除全局变量所带 来的危险。利用分支技术来来封装浏览器之间的差异。可以把代码组织的更为一体,便于阅读和维护。
第7页
Singleton-EXP
UserAgent = function UserAgent() {
var u = navigator.userAgent, d = document;
this.ie = typeof d.all != "undefined";
this.ns4 = typeof d.layers != "undefined";
this.dom = typeof d.getElementById != "undefined";
this.safari = /Safari/.test(u);
this.moz = /Gecko/.test(u) && !this.safari;
this.mie = this.ie && /Mac/.test(u);
this.win9x = /Win9/.test(u) || /Windows 9/.test(u);
this.o7 = /Opera 7/.test(u);
this.supported = (typeof d.write != "undefined")
&& (this.ie || this.ns4 || this.dom);
};
第8页
Singleton-EXP
ua = new function UserAgent() {
var u = navigator.userAgent, d = document;
this.ie = typeof d.all != "undefined";
this.ns4 = typeof d.layers != "undefined";
this.dom = typeof d.getElementById != "undefined";
this.safari = /Safari/.test(u);
this.moz = /Gecko/.test(u) && !this.safari;
this.mie = this.ie && /Mac/.test(u);
this.win9x = /Win9/.test(u) || /Windows 9/.test(u);
this.o7 = /Opera 7/.test(u);
this.supported = (typeof d.write != "undefined")
&& (this.ie || this.ns4 || this.dom);
};
第9页
装饰者(Decorator)模式
动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。
第10页
Decorator-EXP
// Create a Name Space myText = { };
myText.Decorators = { }; // Core base class
myText.Core = function( myString ) {
this.show = function( ) { return myString; };
}
// First Decorator, to add quesCon mark to string
myText.Decorators.addQuestionMark = function ( myString ) {
this.show = function( ){ return myString.show( ) + '?'; };
}
//Second Decorator, to make string Italics
myText.Decorators.makeItalic = functioon( myString ) {
this.show = function(){ return "<i>" + myString.show( ) + "</i>"; };
}
第11页
Decorator-EXP
//Third Decorator, to make first character of sentence caps myText.Decorators.upperCaseFirstChar = function( myString ) {
this.show = function( ){
var str = myString.show( );
var ucf = str.charAt(0).toUpperCase( );
return ucf + str.substr( 1, str.length – 1 ); }; }
// Set up the core String
var theString = new myText.Core( “this is a sample test string” );
// Decorate the string with Decorators
theString = new myText.Decorator.upperCaseFirstChar( theString );
theString = new myText.Decorator.addQuestionMark( theString );
theString = new myText.Decorator.makeItalic( theString );
theString.show();
第12页
桥接(Bridge)模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
第13页
Bridge-EXP
addEvent(element, 'click', getBeerById);
function getBeerById(e) {
var id = this.id;
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// Callback response.
console.log('Requested Beer: ' + resp.responseText);
});
}
第14页
Bridge-EXP
function getBeerById(id, callback) {
// Make request for beer by ID, then return the beer data.
asyncRequest('GET', 'beer.uri?id=' + id, function(resp) {
// callback response
callback(resp.responseText);
});
}
addEvent(element, 'click', getBeerByIdBridge);
function getBeerByIdBridge (e) {
getBeerById(this.id, function(beer) {
console.log('Requested Beer: '+beer);
});
}
第15页
适配器(Adapter)模式
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
第16页
Adapter-EXP
function $(){};
function YAHOO.util.Dom.get=function(el){};
function prototypeToYuiAdapter(){ return YAHOO.util.Dom.get(arguments); }
function YUIToPrototypeAdapter(el) { return $.apply(window, el); }
$ = prototypeToYuiAdapter;
YAHOO.util.Dom.get = YUIToPrototypeAdapter;
第17页
观察者(Observer)模式
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
第18页
Observer-EXP
// The Observer Object – One who super sees all the print operations
function printManager( ) {
var queue = [ ];
// The attach method
this.addJob = function(name, job) { queue.push( { ”name” : name, "job” : job } );
}
// The detach method
this.removeJob = function(job) {
var _queue = [ ];
for(var i in queue) {
if(queue[ i ].job == job) continue; else _queue.push( queue[ i ] );
}
queue = _queue;
}
// The notify method
this.doPrint = function( item ) {
for ( var i in queue ) { queue[ i ].job.call( this, item );}}
}
第19页
Observer-EXP
var p = new printManager(); // Publishers are in charge of "publishing”
function printWithItalics( str ) { // The callback function – the print job
alert( “<i>” + str + “</i>” );
}
//Once subscribers are notified their callback functions are invoked
p.addJob( "italics", printWithItalics);
// Notify the observer about a state change
p.doPrint("this is a test");
第20页
门面(Facade)模式
子系统中的一组接口提供一个一致的界面, 门面模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
第21页
Facade-EXP
function IEBrowser(){this.hello=function(){alert(”IE browser”);}}function NonIEBrowser()(this.hello =function(){alert(“NonIE browser”);}}var Facade={};Facade.hello=function(){var browser;if(window.ActiveXObject)browser=new IEBrowser();else browser=new NonIEBrowser();Browser.hello(););
第22页
策略(Strategy)模式
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
第23页
Strategy-EXP
var Button = function(submit_func, label) {
this.label = label;
return {
on_submit : function(numbers) {
return submit_func(numbers);
}
};
};
var numbers = [1,2,3,4,5,6,7,8,9];
第24页
Strategy-EXP
var sum = function(n) {
var sum = 0;
for ( var a in n ) {
sum = sum + n[a];
}
return sum;
};
var a = new Button(sum, "Add numbers");
var b = new Button(function(numbers) {
return numbers.join(',');
}, "test2");
a.on_submit(numbers);
b.on_submit(numbers);
第25页
命令(Command)模式
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
第26页
Command-EXP
var Calculator={
// addition
add: function(x,y) {
return x+y;
},
// subtraction
substract: function(x, y) {
return x-y;
},
// multiplication
multiply: function(x, y) {
return x*y;
},
// division
divide: function(x, y) {
return x/y;
},
};
第27页
Command-EXP
Calculator.calc=function(command) {
return Calculator[command.type](command.opl,command.op2);
};
Calculator.calc({type: "add",opl:1,op2:1});
Calculator.calc({type: "Substract",opl:6,op2:2});
Calculator.calc({type: "multiply",opl:5,op2:2));
Calculator.calc({type: "divide",opl:8,op2:4));
第28页
职责链(Chain Of Responsibility)模式
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
第29页
组合(Composite)模式
将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
第30页
享元(Flyweight)模式
运用共享技术有效地支持大量细粒度的对象。
第31页
总结
设计模式不是哪种编程语言特有的,它同样可以应用于Javascrip,各种设计模式建立在面向对象编程的基础上,而设计模式应用于前端也是在最近几年,只有通过连续不断的软件开发实践我们才能够灵活的运用设计模式。
第32页
参考资源
http://www.www.digital-web.com/articles/excerpt_pro_javascript_patterns
http://aspalliance.com/1782_Working_with_GoFs_Design_Patterns_in_JavaScript_Programming.7
http://michaux.ca/articles/the-command-pattern-in-javascript-encapsulating-function-property-calls
http://en.wikipedia.org/wiki/Strategy_pattern#JavaScript
http://baike.baidu.com/view/66964.htm
http://www.slideshare.net/rmsguhan/javascript-design-patterns
第33页
谢谢!