这是我参与更文挑战的第16天,活动详情查看: 更文挑战
1. 简介
单例模式说的是只能被实例一次,如果已经实例过了的话,就不需要在实例。看到了这里我想到的是实例过程,首先是一个对象,而且这个对象只能被实例一次。在js存在一次的对象有字面直接量
和模块化具有特定功能的模块对象
(可能这句话说的不严谨,这只是我自己的定义。)
对象直接量
在网上看到了对象直接量也是单例模式的一种,但是它是在window或者包含它的模块执行的时候就已经初始化了。当然也是有着单例模式的对外行为,暴露了自己的接口并且只被实例化了一次(如果后面代码没有被重新赋值的情况下)。但是这种对象的存在太脆弱,并且设计模式如果只是一个对象直接量就可以完成的,那么单例模式存在的意义是什么呢?
所以我更想讲对象直接量是一种行为上的单例对象,而不是我们说的设计模式。
模块化具有特定功能的模块对象
然后再谈一下另外一种存在,模块化具有特定功能的模块对象。前面说到不严谨,是因为模块化的对象也是可能被实例化多次的。所以这个特定功能的意义就在于它的内部自己会检测是否被实例化,如果已经实例化了,就不要在创建新的对象了。
扯点闲篇,requie在加载各个模块的时候是将已经创建的模块缓存在一个数组里,然后在访问某个模块的时候先查找是否已经存在,如果已经存在的话就不会在创建了。那么这也是一种单例模式的行为。但是这个单例模式的意义在于require不想浪费过多的内存去创建新的对象。
可能会有人提到创建新的对象,以前的对象在js引擎检测到这个对象已经没有依赖使用的话会销毁这个对象。垃圾回收的机制一定是要经过自己的检测(可能不止一次)才能确认当前的对象已经不再使用,这也和性能有关系(我猜测)。
说上面的的内容是为了什么呢
是因为设计模式的含义在于提高代码的可重用性,增强系统的可维护性,以及解决一系列的复杂问题。如果我们使用代码的不需要执行多次创建对象,其实是提高了对象的可重用性,我们花费内存创建的对象是要完成一个功能的,不是干了一次活就走人的对象。如果这个模块化需要对于与其他的模块交互的时候需要一些记忆,那么重建的对象肯定是没有了。当然你可能说这个记忆可以交给模块层级之上的system模块来实现,但是这样岂不是增加了系统维护性的负担,我还需要跑到系统对象中去查找。
说到这里可能你会觉得说的不太清楚,可能例子不太好再加上本人的理解不够深入。说上面的例子是为了引出两个关键语言。一个是对象只创建了一次,另一个是记忆的问题。
现在我们把上面的模块化对象上升到system层级上,requie的模块可以由自己的数组来判断是否已经存在,那么system层级上的话就只能自己判断了,所以它的内部会有判断自己是否已经实例化的机制。对于记忆的功能,那是可能的,毕竟是用来关联各个模块之间的通信,那么记忆可定是需要,
但是作为系统对象,一定要具有健壮性的,不是其他的模块可以随意修改的,这也关联到上面说对象直接量的脆弱。因为模块的对象可以将自己的记忆部分不对外暴露。(当然你可能说对象直接量可以将自己的变量进行setter处理,自己进行严格控制,但是如果是这么复杂的话那么我们就应该使用一个完整的模块去维护他,因为我认为开发过程中如果直接使用了对象直接量,开发人员不一定会花过多的时间去思考它的维护性,健壮性,当然这是一家之谈)。
说到这里,我们得到的概念是单例模式的对象是一个只会实例化一次的对象,并且是会记忆内容和具有健壮性,可维护性的对象。
2. 实现
我在design pattern的书中看到一个例子
var SingletonTester = (function () {
//private property.
var _property1 = "something",
var _property2 = "something else",
// 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;for (var i = Things.length - 1; i >= 0; i--) {
Things[i]
}
this.pointY = options.pointY || 10;
}
//flag variable
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
});
/*调用公有的方法来获取实例:*/
Singleton.getInstance().publicMethod();
复制代码
例子中的
- // private property 就是我们的记忆,
- // flag variable. 就是我们只实例化一次的flag.
3. 缺点
在网上看到有说到单例模式的缺点,灵活性不好,不容易扩展。其实只针对于单个的模块对象是容易扩展的,只是因为单例模式的应用在系统层级上影响太大。
但是我想说的是对于一个system层级用户相互通信的模块,它的功能本质上就是为了各模块之间的通信交互的,随着业务的增加,对于单例模式上的功能可能要增加(更多的相信系统层级上的功能是为了模块通信,而不是针对某一个特殊的模块处理)。
如果新的业务增加,这些必要的处理应该是在工具模块中进行处理,而本身系统层级上还是进行以前的api调用就可以了。当然除非是架构上更新,这个system对象不能够运行了,那么肯定是要添加的。
4. 闲谈
对于单例模式的使用相信更多的是将重点放在它的功能上,就是一个我只需要创建一次的对象,是会在我整个项目中起到链接通信的作用。这也是想在我们使用的各种框架中都会先加载框架文件,然后我们的文件运行在它的大环境下,如果这个对象自己动不动创建一下,那我们的系统还要运行不。