意图
保证一个类只有一个实例,并提供一个访问它的全局访问点。
适用性
- 当类只能有一个实例并且客户可以从一个从所周知的访问点访问它。
- 当这个唯一实例应该通过子类来实现可扩展性,并且用户不需要修改代码就可以使用一个扩展的实例。
结构
- Singleton
定义一个Instance操作,允许客户访问它的唯一实例。C++中通常是一个静态成员函数。
可能负责创建它自己的唯一实例。
协作
客户只能通过Singleton的Instance操作访问一个Singleton的实例。
效果
- 对唯一实例的访问控制。
因为Singletion类封装了它的唯一实例,并且客户只能通过Instance接口访问,所以它可以严格控制客户怎样及何时访问它。 - 缩小命名空间污染。
Singleton是对全局变量的一种改进,它避免了那些存储唯一实例的全局变量污染命名空间。 - 允许对操作和表示的精化。
Singleton类可以有子类,而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需类的实例在运行时配置应用。 - 允许可变数目的实例。
这个模式可以使你方便改变想法,允许通过改变实现来控制应用可以使用的实例的数目。 - 比类操作更为灵活。
另一种封装Singleton功能的方式使用类操作(即C++中静态成员函数)。但这会使得难以改变设计以允许一个类有多个实例,C++中静态成员函数不是虚函数,子类不能多态重定义他们。
实现
1) 保证唯一的实例。C++通常将创建实例的操作隐藏在一个类函数后面,它保证只有一个实例被创建。并且需要隐藏构造器来防止客户通过其他方式能创建多个实例。
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* m_instance = 0;
};
Singleton* Singleton::m_instance = 0;
Singleton* Singleton::Instance() {
if (m_instance == 0) {
m_instance = new Singleton;
}
return m_instance;
}
2) 创建Singleton类的子类。指向实例的变量可以用子类的实例来初始化,这样可以创建不同的对象。最简单的技术是在Singleton的Instance操作中决定你要使用哪个单例。
另一个选择是将Instance的实现从父类中分离出来并放入子类。这样允许C++程序员在连接时刻决定单例的类(即通过链入一个包含不同实现的对象文件),但对客户则隐藏了这一点。
一个更灵活的方法是使用一个单例注册表。Singleton类可以根据名字在一个众所周知的注册表中注册它们的单件实例。
这个注册表在字符串名字和单件之间建立映射。当Instance需要一个单件时,它参考注册表,根据名字请求单件。
注册表查询相应的单件(如果存在的化)并返回它。这个方法使得Instance不再需要知道所有的Singleton类或实例。它需要知道的只有所有Singleton类的一个公共接口,该接口包括了对注册表的操作。
class Singleton {
public:
static void Register(const char* name, Singleton*);
static Singleton* Instance();
protected:
static Singleton* Lookup(const char* name);
private:
static Singleton* m_instance;
static List<NameSingletonPair>* m_registry;
};
Register以给定名字注册Singleton实例,Lookup操作根据给定名字查找单例。
示例
// 只生成一个实例的工厂类
class MazeFactory {
public:
static MazeFactory* Instance();
// ...
protected:
MazeFactory();
private:
static MazeFactory* m_instance;
};
MazeFactory* MazeFactory::m_instance = 0;
MazeFactory* MazeFactory::Instance() {
if (m_instance == 0) {
m_instance = new MazeFactory;
}
return m_instance;
}
// 多个子类的情况
MazeFactory* MazeFactory::Instance() {
if (m_instance == 0) {
const char* mazeStyle = getenv("MAZESTYLE");
if (strcmp(mazeStyle, "bombed") == 0){
m_instance = new BombedMazeFactory;
} else if (strcmp(mazeStyle, "enchanted") {
m_instance = new EnchantedMazeFactory;
}
//... other possible subclass
else { // default
m_instance = new MazeFactory;
}
}
return m_instance;
}
相关模式
很多模式可以使用Singleton模式来实现,比如Abstract Factory、Builder、Prototype。