设计模式之单体模式

2042 查看

前言

这是本人的设计模式学习笔记,把自己学习过程中的一些总结和认识记录下来,与诸君共勉。本日为大家带来最简单的模式——单体模式。

基本概念

所谓单体模式,就是为了确保在程序中某一类只有一个实例对象,并提供对该实例的全局访问。更广义地说,单体是一个用来划分命名空间并将一批相关方法和属性组织在一起的对象。下面依然以javascript语言来作进一步阐释。

最简单的单体模式可以认为就是一个对象,把一批关联的方法和属性连接在一起。但js中的对象是易变的,某种程度违反了对扩展开发,对修改封闭的设计原则。单体的成员应该只能通过单体对象来访问,该单体对象可以在各个地方被访问,可以说单体对象为它的成员开创了一个独特的命名空间。

下面给出一个js中利用单体模式的例子:

jsvar myNamespace={
    product:function(){...},
    name:'',
    ...
};
myNamespace.product();
//通过命名空间引用对应的方法和变量

为什么使用单体

单体模式是为了确保有且仅有一个实体对象,在有些场景下我们确实有这样的需求,比如你只希望有一个线程池,一个负载均衡的调度者,等等。一旦有两个或者多个就会成为灾难。再举个例子,你可以为网站中的特定页面利用单体模式包装其行为,比如特定表单的提交。

有人会问为什么我们不使用全局变量。其中的问题在于单体做了更好的封装,并且可以在需要时创建,而非一开始始终存在,而全局变量是始终存在的,这就是所谓的惰性实例化。

最后举个使用单体和分支的例子,你要创建一个xhr对象,但你希望该页面所有的请求都由该xhr对象发出,但你不清楚浏览器类型,则你可以使用单体,单体对象的多个私有方法分别是对不同类型浏览器产生xhr对象的方法,最后实际产生时根据浏览器类型将对应的私有方法赋给指定对象,创建的行为实际与对浏览器不同类型的操作解耦,贯彻对接口而非实现编程。

如何实现单体

对于oo语言中,实现单体的诀窍在于:

  • 类的构造方法必须是私有的,不能为外界所访问的
  • 使用类的静态变量以标记实例对象是否被创建,当然该变量可以直接指向创建的实例对象
  • 使用类的静态方法来返回和创建实例对象

下面讲讲在js中实现单体模式,第一个问题是如何实现私用成员。

jsmyNamespace.singleton=(function(){
    return {...};
})();

使用闭包创建私用成员可以保证该成员不会直接被外部所访问,对数据和方法进行保护和封装。单体类只会被实体化一次。这种单体模式又称模块模式,可以将一批相关方法和属性组织为模块并划分命名空间。

单体的问题

单体同时具有一些重要的问题:

  • 多线程编程时应该对标识变量加锁
  • 两个类加载器可能有机会分别创建自己的单件实例
  • 单件完成两个责任,管理自己的实例并在应用程序中担当角色(类应该只关心前者)
  • 不建议继承单件类