单例模式(Singleton)

日期:2016年06月08日

1. 意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

2. 动机

对一些类来说,只有一个实例是很重要的。虽然系统中可以有很多打印机,但却只应该有一个打印假脱机(printer spooler),只应该有一个文件系统和一个窗口管理器。一个数字滤波器只能有一个A/D转换器。一个会计系统只能专用于一个公司。

我们怎么样才能保证一个类只有一个实例并且这个实例易于被访问呢?一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。

一个更好的办法是,让类自己负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法。这就是Singleton模式。

3. 适用性

在下面的情况下可以使用Singleton模式

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。(这里这条不明白是什么意思)

4. 结构

结构图

5. 参与者

  • Singleton —— 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类的操作(即Smalltalk中的一个类方法和C++中的一个静态成员函数)。 —— 可能辅助创建它自己的唯一实例。

6. 协作

  • 客户只能通过Singleton的Instance操作访问一个Singleton的实例。

7. 效果

Singleton模式有很多优点:

  1. 对唯一实例的受控访问。因为Singleton类封装它的唯一实例,所以它可以严格的控制客户怎样以及何时访问它。
  2. 缩小名空间。Singleton模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染名空间。
  3. 允许对操作和表示的精化。Singleton类可以有子类,并且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。(这里这条不明白是什么意思,Java的单例类可以有子类么??)
  4. 允许可变数目的实例。这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。此外,你可以用相同的方式来控制应用所使用的实例的数目。只有允许访问的Singleton实例的操作需要改变。(在这里添加Java的可变数目实例的实例代码!!)
  5. 比类操作更灵活。另一种封装单件功能的方式是使用类操作(即C++中静态成员函数或者是Smalltalk中的类方法)。但这两种语言技术都难以改变设计以允许一个类有多个实例。此外,C++中的静态成员函数不是虚函数,因此子类不能多态的重定义它们。(不明白C++的什么静态成员函数或是虚函数!!)

8. 实现

下面是使用Singleton模式时所要考虑的实现问题:

  1. 保证一个唯一的实例。
  2. 创建Singleton类的子类。

Singleton类不再负责创建单件。它的主要职责是使得供选择的单件对象在系统中可以被访问。静态对象方法还有一个潜在的缺点——也就是所有可能的Singleton子类的实例都必须被创建,否则他们不会被注册。(完善这部分的内容!!!)

9. 代码示例

10. 已知应用

在Smalltalk-80[Par90]中Singleton模式的例子是改变代码的集合,即ChangeSet current。一个更巧妙的例子是类和它们的元类(metaclass)之间的关系。一个元类是一个类的类,而且每一个元类有一个实例。元类没有名字(除非间接地通过它们的唯一实例),但他们记录了它们的唯一实例并且通常不会再创建其他实例。

InterViews用户界面工具箱[LCI+92]使用Singleton模式在其他类中访问Session和WidgetKit类的唯一实例。Session定义了应用的主事件调度循环、存储用户的风格偏好数据库,并管理与一个或多个物理显示的连接。WidgetKit是一个Abstract Factory,用于定义用户的窗口组件的视感风格。WidgetKit::instance()操作决定了特定的WidgetKit子类,该子类根据Session定义的环境变量进行实例化。Session的一个类操作决定了支持单色还是彩色显示并据此配置单件Session的实例。

11. 相关模式

很多模式可以使用Singleton模式实现。参见Abstract Factory、Builder和Prototype模式。