1.简介
在Ajax技术出现之前,传统的Web应用中JavaScript代码所占的比例较少,并且大多数情况下都是采用面向过程的编程方式,真正将JavaScript作为一门高级语言进行正式编程的开发人员也很少。而目前富客户端的Web应用开发已经成为大势所趋,JavaScript在Web应用中所占的比例会越来越大。如何有效地实现代码的复用以及灵活应对需求的变化已经正式列入JavaScript程序员必须探讨的议事日程。
GoF的《Design Patterns:Elements of Reusable Object-Oriented Software》一书成为当代程序员研究设计模式的典范与开山之作。然而,目前针对设计模式的探讨主要停留在以Java与C#等强类型高级语言方面,那么,对于像JavaScript这样的弱类型语言是否也适用设计模式呢?答案是肯定的。在本文中,我将通过几个简短的示例来向你展示如何把设计模式应用于像JavaScript这样的弱类型语言的编程之中。
2.Singleton模式(亦称“单例模式”)
(1)概念
Singleton模式作为一种非常基本和重要的创建型模式,其职责是保证一个类有且只有一个实例,并提供一个访问它的全局访问点。
在程序设计过程中,有很多情况下需要确保一个类只能有一个实例。传统的编程语言中为了使一个类只有一个实例,最容易的方法是在类中嵌入静态变量,并在第一个实例中设置该变量,而且每次进入构造函数都要做检查,不管类有多少个实例,静态变量只能有一个实例。为了防止类被多次初始化,需要把构造函数声明为私有类型,这样便只能在静态方法里创建一个实例。
在JavaScript中,虽然我们仍然可以指定静态方法来构造对象,但由于我们不能利用构造函数的“私有”来禁止多个实例的生成,因此要完全实现Singleton并没有想象中那么简单。
(2)举例
假设有一个作为工具类使用的对象,它不包含任何业务逻辑,也不包含任何需要改变的数据。使用这样的对象时不需要每次都进行实例化,只需要存在一个全局的对象供调用即可。
根据刚才的分析,我们就可以在JavaScript中通过如列表1所示的方式实现Singleton模式。模式可以保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
列表1:在JavaScript中创建Singleton模式类
function Singleton(){
this.property1=”hello”;
this.methodl=function(x,y){
return x+y;
}
//定义一个静态属性
Singleton._instance_=null;
Singleton.getInstance=function(){
if(this._instance_==null){
//如果Singleton实例不存在,则进行实例化
this._instance_=new Singleton();
}
//返到Singleton的对象实例
return this._instance_;
};
上面的getInstance方法首先判断静态属性Singleton._instance_是否为null;如果为null,则新建一个Singleton类的对象,保存在Singleton._instance_,最后返回Singleton._instance_属性。
至于Singleton类的使用方法则很简单:
var instA =Singleton.getInstance(); //返回Singleton类的唯一实例
但遗憾的是,以上的代码不能禁止用户直接实例化Singleton类。例如,通过以下语句实例化Singleton类仍然是成立的:
var instA =new Singleton ();
而随后你调用如下语句也是成立的:
alert(typeof(instA));
alert(instA.property1);
因此,我们需要进一步修改Singleton类的构造函数,使得Singleton类只能在Singleton.getInstance方法中进行实例化。
function Singleton(){
if(Singleton.caller!=Singleton.getInstance){
throw new Error(”Can not new Singleton instance!”);
}
…………
}
这样当用户试图自己创建多个对象的时候,通过人工抛出异常来阻止。不过这么做你感觉是不是还是有一点别扭?创建单例就创建单例吧,干嘛非要抛出一个异常呢?但无论如何,使用上述方案已经能够实现了仅能创建类的单例的目的。
下面,我们来讨论第三种方法(也是最优秀的方法)。这种方法巧妙利用了JavaScript对于“匿名”函数的支持特征来间接实现禁止对Singleton类构造函数的访问,从而较好地模拟了私有构造函数的特性,最终比较完美地解决了用JavaScript实现Singleton模式的问题。
列表2:借助于“匿名”函数的支持在JavaScript中创建Singleton模式类
<script type="text/javascript">
(function(){
//SingletonFactory Interface
SingletonFactory = {
getInstance : getInstance
}
//private classes
function SingletonObject()
{
SingletonObject.prototype.methodA = function()
{
alert('methodA');
}
SingletonObject.prototype.methodB = function()
{
alert('methodB');
}
SingletonObject.instance = this;
}
//SingletonFactory implementions
function getInstance()
{
if(SingletonObject.instance == null)
return new SingletonObject();
else
return SingletonObject.instance;
}
})();
var instA = null;
try
{
alert("试图通过new SingletonObject()构造实例!");
instA = new SingletonObject();
}
catch(e){
alert("SingletonObject构造函数不能从外部访问,系统抛出了异常!");
}
instA = SingletonFactory.getInstance(); //通过Factory上定义的静态方法获得
var instB = SingletonFactory.getInstance();
instA.methodA();
instB.methodA();
alert(instA == instB); //成功
</script>
上面的第三种创建方式可谓挖空心思,穷JavaScript之能事,但终于获取了令人满意的效果。
3.Factory Method模式
(1)概念
根据GoF的定义,Factory Method模式的目的是为了定义一个创建对象的接口,由子类来决定实例化哪一个类。更准确地说应该是,Factory Method模式是将创建对象实例的责任转移到工厂类中,并利用抽象的原理,将实例化行为延迟到具体工厂类中。
(2)举例
在某些情况下,我们的确无法确定将要创建的对象是哪个类的实例,这样的一个典型示例就是在Ajax应用程序中需要创建XMLHttpRequest(XHR)对象时,因为大家都知道在不同的浏览器中XHR对象的实现类是不同的。
通过引入Factory Method模式,我们即可以轻松使用JavaScript实现创建通用的XHR对象,相应的代码实现如下所示。
function XMLHttpFactory(){}
XMLHttpFactorv.CreateXMLHttp=function() {
if(typeof XMLHttpRequest!=”undefined”){
//支持XMLHttpRequest对象的浏览器:例如Firefox等
return new XMLHttpRequest();
}else if(typeof window.ActiveXObject!=”undefined”){
//支持ActiveX对象的浏览器,即IE
return new ActiveXObject(“MSXML2.XMLHttp”);
}
}
然后,通过以下代码便可以相当容易地判断出浏览器的类型并进而创建XHR对象。
var xmlhttp=XMLHttpFactory.createXMLHttp();
此后,基于以上创建的XHR对象就可以进行Ajax风格的服务器端调用了。
4.Decorator模式
(1)概念
Decorator模式是结构型模式的一种,它充分利用了继承与聚合的优势,其主要目的是为了给一个对象动态地添加以新的职责,而此职责并不修改原有的行为,而是在原有行为基础上添加新的功能,就好比装饰工人为一座新居的墙上涂抹上色彩缤纷的颜料一般。
【备注】面向方面编程(AOP),是一种改进已经存在的模式和发现新模式的方法。面向方面的编程能够独立于任何继承层次结构而应用改变类或对象行为的方面。然后,在运行时或编译时应用这些方面。AOP编程引入了一组新概念:接合点(Joinpoint)—代码中定义明确的可识别的点。切点(Pointcut)—通过配置或编码指定接合点的一种方法。通知(Advice)—表示需要执行交叉切割动作的一种方法。混入(Mixin)—通过将一个类的实例混入目标类的实例引入新行为。对于好的编程来说,设计模式已经变成了常用的方式。AOP可以给我们实际上,横切关注点代码的注入,因此说它也就是一种新式类型的软件设计模式。
(2)举例
有意思的是,我们也可以把Decorator模式应用于JavaScript中实现面向方面编程(AOP)。现在,不妨让我们观察一下列表3中定义的代码。
列表3:使用Decorator模式在JavaScript中实现面向方面编程(AOP)
function Decorator(object){
object.setup=function(method){
//如果从未执行过Setup方法,则进行初始化
if(!(‘_’+method in object)){
//保留原有的method方法
object[‘_’+method]=object[method];
//初始化前置和后置通知的列表
object[‘beforeAdvice_’+method]=[];
object[‘afterAdvice_’+method] =[];
//改写method方法
object[method]=function(){
//前置通知列表
var before=this[‘beforeAdvice_’+method];
//后置通知列表
var after=this[‘afterAdvice_’+method];
//返回值
var ret;
//执行前置通知中的函数
for(var i=0; i<before.length; i++) {
before[i].call(this,arguments);
}
//执行原有的方法
ret=this[‘_’ +method](arguments);
//调用后置通知中的函数
for (var i=; i<after.1ength; i++){
after[i].call(this, arguments);
}
//返回原有方法的返回值
return ret;
}
}
};
//添加前置通知
object.addBeforeAdvice=function(method, f) {
object.setup(method);
//最后添加的前置通知将最先被执行
object[‘beforeAdvice_’ +method].unshift(f);
};
//添加后置通知
object.addAfterAdvice=function(method, f) {
object.Setup(method);
//最后添加的后置通知将最后被执行
object[‘afterAdvice_’ +method].push(f);
};
}
Decorator函数的基本思路是将原有的函数替换,在替换后的函数中增加对前置、后置通知的调用。下面通过一个具体的示例来说明Decorator函数的使用方法,首先定义一个类testClass。
var testClass = function() {
testClass.prototype.pl=”hello”;
testClass.prototype.ml =function() {
alert(this.p1);
}
};
如果希望在所有testClass类的实例上增加前置或后置通知,那么需要对testClass.prototype属性进行处理。
Decorator(testClass.prototype);
testClass.prototype.addBeforeAdvice(“m1”, function() {
alert(“beforeAdvice”);
});
testClass.prototype.addAfterAdvice(“m1”, function() {
alert(”afterAdvice”);
});
此时,创建一个testClass类的对象实例,并且执行它的ml方法,程序将依次输出“beforeAdvice”、“hello”和“afterAdvice”。
var t=new testClass ();
t.ml(); //依次输出“beforeAdvice”、“hello”和“afterAdvice”
如果仅希望对testClass类的某个具体实例添加前置、后置通知,那么直接处理该实例即可。
var t=new testClass();
Decorator(t);
t.addBeforeAdvice(“ml” ,function](){
alert(“beforeAdvice”);
});
t.addAfterAdvice(“ml”, function(){
alert(“afterAdvice”);
});






