问题聚焦:
让类自身负责保存它的唯一实例,并且保证没有其他实例可以被创建。
单例模式使用起来比较简单,但是当单例类有子类并且数目不定时,需要用一个注册表管理它,这一部分讲的不清楚,以后补充。
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
动机:
一个数字滤波器只能有一个A/D转换器,一个会计系统只能专用于一个公司。
要求:保证一个类只有一个实例,并且这个实例易于被访问
一个全局变量是得一个对象可以被访问,但是不能防止你实例化多个对象
方案:让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。这就是单例模式。
适用性:
下面的情况可以使用单例模式:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时
结构:
参与者:
Singleton:
- 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作(即C++中的一个静态成员函数)。
- 可能负责创建它自己的唯一实例。
协作:
客户只能通过Singleton的Instance操作访问一个Singleton的实例。
效果:
单例模式的优点:
对唯一实例的受控访问:严格控制客户怎样以及何时访问它
缩小名空间:全局变量的一种改进,避免了那些存储唯一实例的全局变量污染命名空间
允许对操作和表示的精化:Singleton类可以有子类,而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。
允许可变数目的实例:允许Singleton类的多个实例,此外,可以用相同的方法来控制应用所使用的实例的数目。
比类操作更灵活:另一种封装单件功能的方式是单纯使用类操作(C++中的静态成员函数),但是难以改变设计以允许一个类有多个实例。
实现:
下面是使用Singleton模式时所要考虑的实现问题
1) 保证一个唯一的实例:创建这个实例的操作隐藏在一个类操作后面,由它保证只有一个实例被创建
Demo:
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton(); // 构造函数是protected型的,视图直接实例化Singleton的客户将得到一个编译时的错误信息。
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance() {
if (_instance == 0) {
_instance = new Singleton; //惰性初始化,直到第一次使用这个实例时才初始化。
}
}
客户仅通过Instance成员函数访问这个单件。
关于c++的实现还有一点要注意,将单件定义为一个全局或静态的对象,然后依赖于自动的初始化是不够的,有三个原因:
- 我们不能保证静态对象只有一个实例会被声明
- 我们可能没有初始化一个实例所需要的每个信息
- C++没有定义转换单元上全局对象的构造器的调用顺序
- 无论单件是否用到,全局/静态对象都会被创建
2) 创建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* _instance;
static List<NameSingletonPair>* _registry; //每个NameSingletonPair将一个名字映射到一个单例。
};
Lookup操作根据给定单例的名字进行查找,我们假定一个环境变量指定了所需要的单件的名字。
Singleton* Singleton::Instance () {
if (_instance == 0)
{
const char* singletonName = getenv("SINGLETON");
_instace = Lookup(singletonName);
}
return _instance;
};
Singleton类的注册的两种方法:
1 构造函数中注册:
MySingleton::MySingleton() {
Singleton::Register("MySingleton*, this");
}
2 定义一个静态实例来实现
static MySingleton theSingleton;
Singleton类不再负责创建单例,它的主要职责是是得供选择的单例对象在系统中可以被访问。
静态对象方法有一个缺点:所有可能的Singleton子类的实例都必须被创建,负责它们不会被注册。
代码示例:
用单例模式重写MazeFactory类。
不考虑单例子类的情况
class MazeFactory {
public:
static MazeFactory* Instance();
protected:
MazeFactory();
private:
static MazeFactory* _instance;
};
MazeFactory* MazeFactory::_instance = 0;
MazeFactory* MazeFactory::Instance ()
{
if (_instance ==0) {
_instance = new MazeFactory;
}
}
当存在MazeFactory的多个子类。而且应用必须决定使用哪一个子类时的情况。
在本例中,我们将通过环境变量选择迷宫的种类并根据该环境变量的值增加代码用于实例化适当的MazeFactory子类。
MazeFactory* MazeFactory::Instance ()
{
if(_instance == 0)
{
const char* mazeStyle = getenv("MAZESTYLE");
if (strcmp(mazeStylem, "bombed") == 0)
{
_instance = new BombedMazeFactory;
}
else if (strcmp(mazeStyle, "enchanted") == 0)
{
_instance = new EnchantedMazeFactory;
}
// ... ... other possible subclass
else
{
_instance = new MazeFactory;
}
}
return _instance
}