3.5-Singleton-单例模式-对象创建模式

用途

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

示例

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

适用性

  • 唯一实例应该是通过子类化可扩展的,并且客户应该无须更改代码就能使用一个扩展的实例时。
  • 类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

结构

  • Singleton:定义一个 Instance 操作,允许客户访问它的唯一实例。Instance 是 一个类操作(即 Java 中的一个类方法和 C++中的一个静态成员函数)。

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

优缺点

  • 对唯一实例的受控访问:因为 Singleton 类封装它的唯一实例,所以它可以严格地控制客户怎样以及何时访问它。
  • 缩小名字空间:Singleton 模式是对全局变量的一种改进,它避免了那些存储唯一实例的全局变量污染名字空间。
  • 允许可变数目的实例:可以用相同的方法来控制应用所使用的实例的数目。
  • 比类操作更灵活:C++等语言不允许一个类有多个实例。此外,C++中的静态成员函数不是虚函数,因此子类不能多态地重定义它们。

实现

类型定义 方法实现:在 Instance 方法中懒初始化

  • 注意构造器是 protected 的。试图直接实例化 Singleton 的客户将得到一个编译时的错误信息。这就保证了仅有一个实例可以被创建。

  • 关于全局变量法实现单例的问题:在 C++中将单件定义为一个全局或静态的对象,然后依赖于自动初始化,这是不够的(应当使用静态成员函数)。这是因为:

    • 不能保证静态对象只有一个实例会被声明
    • 可能没有足够的信息在静态初始化时实例化每一个单件(例如初始化需要依赖后续计算的值)
    • C++没有定义转换单元(translation unit)上全局对象的构造器的调用顺序
  • 使用一个 Singleton 创建不同类型的单件

    • 由 Singleton 派生子类,不同子类用于创建不同单件
    • 对于 C++:在不同的文件中创建不同的 Singleton 的实现,然后链接时选择具体的实现。
    • 使用单件注册表:可能的 Singleton 类的集合不是由 Instance 定义的。Singleton 类可以根据名字在一个众所周知的注册表(存储实例名→单件实例的映射关系)中注册它们的单件实例;收到请求时查询相应的单件(如果存在的话)并返回它。
      • Singleton 类不再负责创建单件。它的主要职责是使得供选择的单件对象在系统中可以被访问
      • 所有可能的 Singleton 子类的实例都必须被创建,否则它们不会被注册

如何实现单件注册表:

  • Singleton 类提供注册、查询、创建实例的接口
  • Singleton 类可以在其构造器中注册自己。例如, MySingleton 子类可以像下面这样做(需要在包含 MySingleton 实现的文件中以静态实例的形式创建该对象,从而调用注册方法):