单例模式
在JavaScript语言中, 单例服务作为一个从全局空间的代码实现中隔离出来共享的资源空间是为了提供一个单独的函数访问指针。
我们能像这样实现一个单例:
var mySingleton = (function () {
// Instance stores a reference to the Singleton
var instance;
function init() {
// 单例
// 私有方法和变量
function privateMethod(){
console.log( "I am private" );
}
var privateVariable = "Im also private";
var privateRandomNumber = Math.random();
return {
// 共有方法和变量
publicMethod: function () {
console.log( "The public can see me!" );
},
publicProperty: "I am also public",
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// 如果存在获取此单例实例,如果不存在创建一个单例实例
getInstance: function () {
if ( !instance ) {
instance = init();
}
return instance;
}
};
})();
var myBadSingleton = (function () {
// 存储单例实例的引用
var instance;
function init() {
// 单例
var privateRandomNumber = Math.random();
return {
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// 总是创建一个新的实例
getInstance: function () {
instance = init();
return instance;
}
};
})();
// 使用:
var singleA = mySingleton.getInstance();
var singleB = mySingleton.getInstance();
console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true
var badSingleA = myBadSingleton.getInstance();
var badSingleB = myBadSingleton.getInstance();
console.log( badSingleA.getRandomNumber() !== badSingleB.getRandomNumber() ); // true
在四人帮(GoF)的书里面,单例模式的应用描述如下:
1、每个类只有一个实例,这个实例必须通过一个广为人知的接口,来被客户访问。
2、子类如果要扩展这个唯一的实例,客户可以不用修改代码就能使用这个扩展后的实例。
关于第二点,可以参考如下的实例,我们需要这样编码:
mySingleton.getInstance = function(){
if ( this._instance == null ) {
if ( isFoo() ) {
this._instance = new FooSingleton();
} else {
this._instance = new BasicSingleton();
}
}
return this._instance;
};
区分类的静态实例和单例模式很重要:尽管单例模式可以被实现成一个静态实例,但是单例可以懒构造,在真正用到之前,单例模式不需要分配资源或者内存。
如果我们有个静态对象可以被直接初始化,我们需要保证代码总是以同样的顺序执行(例如 汽车需要轮胎先初始化)当你有很多源文件的时候,这种方式没有可扩展性。
单例模式和静态对象都很有用,但是不能滥用-同样的我们也不能滥用其它模式。
在实践中,当一个对象需要和另外的对象进行跨系统协作的时候,单例模式很有用。
下面是一个单例模式在这种情况下使用的例子:
var SingletonTester = (function () {
// options: an object containing configuration options for the singleton
// e.g var options = { name: "test", pointX: 5};
function Singleton( options ) {
// set options to the options supplied
// or an empty object if none are provided
options = options || {};
// set some properties for our singleton
this.name = "SingletonTester";
this.pointX = options.pointX || 6;
this.pointY = options.pointY || 10;
}
// our instance holder
var instance;
// an emulation of static variables and methods
var _static = {
name: "SingletonTester",
// Method for getting an instance. It returns
// a singleton instance of a singleton object
getInstance: function( options ) {
if( instance === undefined ) {
instance = new Singleton( options );
}
return instance;
}
};
return _static;
})();
var singletonTest = SingletonTester.getInstance({
pointX: 5
});
// Log the output of pointX just to verify it is correct
// Outputs: 5
console.log( singletonTest.pointX );
尽管单例模式有着合理的使用需求,但是通常当我们发现自己需要在javascript使用它的时候,这是一种信号,表明我们可能需要去重新评估自己的设计。
这通常表明系统中的模块要么紧耦合要么逻辑过于分散在代码库的多个部分。单例模式更难测试,因为可能有多种多样的问题出现,例如隐藏的依赖关系,很难去创建多个实例,很难清理依赖关系,等等。
《学用 JavaScript 设计模式》学习资料来源,感谢各位专家们的翻译!
http://www.oschina.net/translate/learning-javascript-design-patterns