[译文]设计模式06 – 单例模式(附代码实例)

原文网址:Singleton Pattern

意图

  • 确保一个类只存在一个实例,并供全局访问
  • 适时初始化,或者在第一次使用时初始化

问题

应用程序需要一个,并且只需要一个对象的实例。另外,延迟初始化和全局访问是必要的。

讨论

单例的职责有:创建、初始化,访问和执行。声明一个私有的、静态的成员变量;提供一个公共的、静态的方法来封装初始化代码,并访问该实例。

客户端可以随时调用这个静态方法来获取单例。

只有在同时满足以下三个条件时才考虑使用单例模式:

  1. 单例的所有权不能被合理地指定
  2. 延迟初始化是值得的
  3. 除此之外没有别的全局访问点

如果单例的所属、初始化的时间和方式以及全局访问都不是问题,那么单例模式就没那么大吸引力了。

单例模式可以被扩展为支持应用程序指定数量的实例。

“静态的成员方法”这种方式不支持子类化。

删除单例是一个重要的设计问题。详见《消灭单例》

结构

 

单例的类是可以直接访问的,并且在第一次使用时初始化。单例是私有的、静态的属性。获取单例的函数是公共的、静态的。

 

举例

美国的总统办公室就是一个单例。美国的宪法规定了总统的选举、任期和继任的章程。任何时间只会有一个在职总统。不管现任总统的个人身份是什么,在全球范围内“美国总统”指代的就是坐在那个办公室里的人。

 

核查清单(也可以理解为应用步骤)

  1. 在实现单例的类里定义一个私有的、静态的属性
  2. 相同的类里,定义一个公共的、静态的方法访问这个属性
  3. 在这个静态方法中实现“延迟初始化”(在第一次使用时初始化)
  4. 将该类的构造函数定义成 protected 或 private
  5. 客户端仅通过第2步中的公共方法使用单例

经验法则

  • 门面模式通常是单例的,因为只需要一个“门面”
  • 状态模式通常是单例的
  • 作为全局变量的“单例”的优势是你能精确掌握实例的数量,如果你想的话,可以管理任意数量的实例
  • 单例模式是被误用最多的模式。当一个类有且仅有一个实例——不多也不少——时才应该使用单例。设计师经常错误地用单例去代替全局变量。总而言之,单例是一个全局变量。单例没有抛弃“全局”的概念,仅仅是另一种表达。
  • 什么时候单例是不必要的?简单来说,是大部分时候。具体来说就是:当传递一个对象引用比把它设为全局变量更简单的时候。单例模式的真正问题在于它给了你一个好的借口——不用仔细地考虑这个对象合适的可见性。找到合理的“暴露-保护”的平衡是维持灵活性的关键。
  • 我们团队在使用全局变量时有一个坏习惯,所以我建了一个“单例”的研究小组。接着我就发现到处都是单例模式,而全局变量引起的问题并没有解决。解决全局变量问题的方法不是把它变成单例,而是要问问自己到底为什么要使用全局变量。改个名字并不能解决问题。事实上,可能更糟,因为你会说“我没用全局变量,而是用了单例”,尽管这两个是一回事。

(译者注:详细的代码实例请移步 GitHub

加入讨论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据