在未读《JavaScript设计模式》这本书前,在我的印象里,单例模式就是每个类只会产生一个实例,非常简单。在细看到这个模式时候,有些疑惑单例模式与工厂模式的区别,虽然看起来像最大区别在于是否多次实例化。
单例(Singleton)模式
单例模式它限制了类的实例化次数只能一次。在实例不存在的情况下,可以通过一个方法创建一个类来实现创建类的新实例;如果实例已经存在,它会简单返回该对象的引用。(这跟我想的一样)
例子:
var mySingleton = (function () { // Instance stores a reference to the Singleton var instance; function init() { // Singleton // Private methods and variables function privateMethod(){ console.log( "I am private" ); } var privateVariable = "Im also private"; var privateRandomNumber = Math.random(); return { // Public methods and variables publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public", getRandomNumber: function() { return privateRandomNumber; } }; }; return { // Get the Singleton instance if one exists // or create one if it doesn't getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })();
特别地方:
1. 单例不同于静态类(或对象),因为我们可以推迟它们的初始化(通常是因为它们需要的参数信息在类定义时是无法获得的),直到需要使用静态实例时,无需使用资源或内存。
2. 单例的唯一实例能够通过子类去扩展,使用者不用更改代码就能使用一个扩展的实例。
3. 单例将导致测试变困难,不利于单元测试。
何时使用单例(When it really is a singleton):
一个类是否是一个单例,必须确定下面三点:
1. Will every application use this class exactly the same way? (exactly is the key word)
2. Will every application ever need only one instance of this class? (ever and one are the key words)
3. Should the clients of this class be unaware of the application they are part of?
PS:这三点是由文章http://www.ibm.com/developerworks/webservices/library/co-single/index.html提出。
我的理解是:
1. 是否每个应用程序使用这个类的方式都完全相同?
2. 是否每个应用程序任何时候都只需要一个类的实例?
3. 使用者不需要知道该类是这应用程序的哪个部分?(或许翻译不好)
当满足这三个点,那就可以用单例。关键就在于类的使用方式都相同,并且不需要应用上下文。
工厂(Factory)模式
工厂模式提供一个通用的接口来创建对象。
例子:
function Car( options ) { this.doors = options.doors || 4; this.state = options.state || "brand new"; this.color = options.color || "silver"; } function Truck( options){ this.state = options.state || "used"; this.wheelSize = options.wheelSize || "large"; this.color = options.color || "blue"; } function VehicleFactory() {} VehicleFactory.prototype.createVehicle = function ( options ) { switch(options.vehicleType){ case "car": this.vehicleClass = Car; break; case "truck": this.vehicleClass = Truck; break; //defaults to VehicleFactory.prototype.vehicleClass (Car) } return new this.vehicleClass( options ); }; var carFactory = new VehicleFactory(); var car = carFactory.createVehicle( { vehicleType: "car", color: "yellow", doors: 6 } ); console.log( car instanceof Car ); // Outputs: true
何时使用工厂:
1. 当对象或组件设置非常复杂的时候。
2. 当需要根据所在的不同环境轻松生成对象的不同实例时。
3. 当处理很多共享相同属性的小型对象或组件时。
4. 对象的实例只需要满足一个约定——鸭子类型(duck typing)。利于解耦。
PS:鸭子类型——“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
抽象工厂(Abstract Factory)模式
书中描述:
使用抽象工厂模式场景:一个系统必须独立于它所创建的对象的生成方式,或它需要与多种对象类型一起工作。
PS:我觉得没描述清楚。
网上定义:
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
PS:这个才比较清晰。
我的理解:
在工厂模式的加上一层抽象,工厂的工厂。
总结
单例模式和工厂模式概念上是比较简单,但是在使用上需要留心。该不该用单例,就考虑上面讲的单例三要点;该不该用工厂,就要看它是否带来大量不必要的复杂性,带来不必要的开销(不为了用模式而用模式)。抽象工厂模式并没有找到好的示例,就先略下。
另外越读这本书,越觉得在某些译文上有些问题,太过于直译,我觉得应该要结合理解做翻译,不然就像外国人讲普通话,太怪了。
参考文献
1. 《Learning JavaScript Design Patterns》 by Addy Osmani
https://addyosmani.com/resources/essentialjsdesignpatterns/book/
2. 《JavaScript设计模式》by 徐涛【译】
本文为原创文章,转载请保留原出处,方便溯源,如有错误地方,谢谢指正。