对于拥有传统的面向对象知识背景的程序员来讲,也许他们懂得许多设计模式的知识。设计模式就是人们将对于特定问题的编程解决方案总结并整理出来,使开发人员无需再对相同的问题伤脑筋。其基本的思想就是开发人员在实现大的解决方案时有许多简单的问题,而通过标准的模式可以将开发人员从不断地重复处理简单问题的泥潭中解脱出来。
现在市面上由许多介绍设计模式的书,大多数著作都是专注于可用于任何面向对象语言的模式。虽然Javascript不可归于此类,但这一系列的文章将会探索在Javascript中应用的流行的设计模式。
创建模式
第一种设计模式将探讨创建模式。正如其名,创建模式处理的就是在程序中创建对象的问题。通常在javascript中创建对象的方式如下:
var oMyObject = new MyClass();
许多时候你没有必要用其他的方式,仅仅需要定义你的类并实例化它。但是有时它并不符合你的设想。
工厂模式
假设你在运行时才知道要调用的类。对于Javascript来说,浏览器差异会造成这样的情况。对于IE浏览器,你需要区创建类的一个实例;对于非IE浏览器,你需要创建另外一个类的实例。实现的关键是由于返回的对象具有相同的接口,所以可以忽略到底时哪个类被实例化。由于Javascript是弱类型的语言而且没有接口的概念,你必须相信返回的对象拥有你需要的属性或方法。现在最好的例子就是创建XMLHttp对象。很多时候你会看到下面的代码:
if (typeof XMLHttpRequest != "undefined") {
return new XMLHttpRequest();
} else if (typeof window.ActiveXObject != "undefined") {
return new ActiveXObject("MSXML2.XMLHttp");
}
显然,你不希望在每次创建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 oXMLHttp = XMLHttpFactory.createXMLHttp();
如果有其他可能的情况需要考虑,则对此处的代码进行更改即可,而不需要影响到其他调用到XMLHttp对象的代码。
在许多面向对象的语言中,工厂返回的类通常会采有私有或者保护的构造函数,那样对象不会在类或者包外的其他地方被直接创建。由于Javascript没有非公有构造函数的概念,在必要时你也许需要采取其他的方式。
假如你有一个叫MyClass的类,它应该只可以通过工厂方法来创建, MyFactory.createObject(),例如
function MyClass() {
this.myproperty = "hello world";
}
function MyFactory {}
MyFactory.createObject = function () {
return new MyClass();
}
同时你希望避免开发人员以这样的方式创建:
var oMyObject = new MyClass();
实际上你无法阻止开发人员通过这种方式来写代码。不像传统语言那样可以通过protected或private的构造函数来保护,没有方式可以捕捉这种非预期的方式。而你需要利用出错信息来通知开发人员这些是非法的操作。你如何做到呢?
重新检查一下代码,你希望去做的是保证MyClass类的实例只能在MyFactory.createObject()方法内创建。可以有办法在运行时当有函数或者方法尝试创建对象的时候通知程序员么?答案是可以的。
每个方法都有一个caller属性指向调用自己的函数,所以你需要做的就是保证caller是不是工厂方法,如果不是的话抛出异常,如下:
function MyClass() {
if (MyClass.caller != MyFactory.createObject) {
throw new Error("There is no public constructor for MyClass.");
}
this.myproperty = "hello world";
}
在这些代码中,你可以通过检查caller属性来确定构造函数是通过MyFactory.createObject()方法调用。如果caller是其他的函数,异常就会抛出并且说明这个类无公有构造函数。虽然在解释型语言中无私有或保护的构造函数,使用这样的工厂模式可以达到同样的效果。
工厂模式的要点:
- 所有从函数或方法返回的对象拥有相同的接口。
- 如果可以的话,对象应不可在工厂方法外直接实例化。但是对于内建的对象如XMLHttpRequest或ActiveXObject就不可能了。
- 工厂方法应用在所有你正常实例化对象的地方。
单例模式
假设你拥有一个对象具有仅作为工具:没有任何商业逻辑,也没有任何会变更的数据。其生命期的唯一功能就是为程序的其他部分提供功能支持。是否必要在此功能每次需要的时候都创建一个新的实例?答案是否定的。仅仅拥有一个对象的实例就够了。
单例模式通过定义一种方法来控制从已确定类创建类的实例数量。通常会定义getInstance()方法。当开发人员需要这些实例的时候,getInstance方法就会被调用。类的静态属性会保存这个类的实例。当getInstance()方法被调用时,首先程序会去检查这个静态属性以确定这个实例是否存在。如果存在这个实例就会被返回,反之新的实例将会被创建斌保存在属性中并且被返回。在javascript中,代码如下:
function MyClass() {
this.myproperty = "hello world";
}
MyClass.__instance__ = null; //define the static property
MyClass.getInstance = function () {
if (this.__instance__ == null) {
this.__instance__ = new MyClass();
}
return this.__instance__;
}
如此定义简单的类后,静态属性__instance__就被定义并且设为空。在静态方法getInstance中,检察这个实例是否为空,如果为空则创建此实例,并赋给它。如此存储的实例将作为函数的返回值返回。开发人员可如以下方式调用:
var oMyObject = MyClass.getInstance();
也许你想你应该防止用户如此调用:
var oMyObject = new MyClass();
你的想法是对的,你可以利用工厂模式的想法:
function MyClass() {
if (MyClass.caller != MyClass.getInstance) {
throw new Error("There is no public constructor for MyClass.");
}
this.myproperty = "hello world";
}
几乎和工厂模式相同的代码;而且是经过验证的代码。
理解单例模式的关键:
- 只有一个实例将会被创建。
- 与工厂模式稍有不同,单例模式通常创建特定类的对象。
· 对象的创建应都通过单例模式的函数创建。
结语
在这篇文章中,我们探讨了一些创建的模式---如工厂模式和单例模式。单例模式就是运行时通过特定的构造函数返回有特定约束的合适的对象。这些对象都应实现开发人员所需要的接口。单例模式就是用于需要保证只有一个实例被创建。同时我们还借这两种模式了解到如何模仿保护的构造函数的实现。
About the Author
Nicholas C. Zakas 是web应用程序的用户界面设计师并且是Professional Ajax和Professional JavaScript for Web Developers的作者。可通过其主页(http://www.nczonline.net/)联系作者本人,在主页中还提供了开源的Javascript类库和工具。
JS设计模式之创建模式
本文探讨了JavaScript中的两种创建模式——工厂模式和单例模式。工厂模式通过一个函数或对象返回所需实例,确保对象拥有相同的接口。单例模式则确保整个程序中只有一个实例存在。
316

被折叠的 条评论
为什么被折叠?



