[学习笔记]设计模式之Singleton(一)

2014-11-24 02:08:35 · 作者: · 浏览: 4
在前几篇笔记中,我们有了解了部分对象创建型模式,包括Builder(建造者)、Abstract Factory(抽象工厂)和Factory Method(工厂方法),今天我们要接触到的是另一种对象创建型模式,既简单又重要的:Singleton(单例)模式。
对一些类来说,只有一个实例是很重要的。比如说,一个软件 系统中,应该只有一个窗口管理器;通信设备中,每张板卡上唯一的端口管理器;一个数字滤波器只能有一个A/D转换器。此外,这些唯一的实例还有一个特点是易于访问。我们知道,用一个全局变量可以使得一个对象可以容易被访问,但它并不能防止我们实例化多个对象,同时也会污染命名空间,并非最佳选择。那么如何才能保证一个类只有一个实例并且这个实例易于被访问呢?让我们来了解一下Singleton模式吧!
要点梳理
目的分类
对象创建型模式
范围准则
对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
主要功能
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用情况
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
参与部分
Singleton:定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作(即C++中的一个静态成员函数)。
协作过程
客户只能通过Singleton的Instance操作访问一个Singleton的实例
示例分析 - 泰坦,世界管理者
让我们重新回到时の魔导士的故事世界中去。在Builder模式一节中,他曾利用WorldCreator来创造平行世界(如果读者从这里开始觉得有点不知所云,可以利用前文的传送门翻阅一下之前的笔记),但是为了使世界更加丰富多彩,魔导士不得不亲力亲为地去设计并改造每一处被创建的世界(譬如给白雪公主一行提供的美食工厂等),这无疑是很巨大的工作量。“如果能有人肯帮我做这些琐事,那么我可以多花点时间做些更美妙的研究啦。”魔导士突然灵光一闪,他想到了组建一个议会。在每一个平行世界中,组建起一个足够强大且独一无二的议会,并让它担任起改造世界,维持生态系统良性运转的重任。时の魔导士将它命名为WorldMgr(世界管理者):
复制代码
1 class WorldMgr {
2 public:
3 static WorldMgr* getInstance();
4
5 //Existing interface goes here
6 protected:
7 WorldMgr();
8 private:
9 static WorldMgr* _instance;
10 }
复制代码
以及它的一个简单实现:
复制代码
1 WorldMgr* WorldMgr::_instance = 0;
2
3 WorldMgr* WorldMgr::getInstance() {
4 if (_instance == 0) {
5 _instance = new WorldMgr();
6 }
7 return _instance;
8 }
复制代码
为了能更好地理解Singleton模式,我们必须详细地解释下上面的代码部分。首先我们利用到静态成员_instance来保存唯一的实例,同时采用静态的getInstance操作提供一个外部访问实例的唯一接口,以此实现了一个Singleton类。其次,我们对构造器加以了保护,以防止WorldMgr被意外的实例化,因为意外的实例化可能会导致多个实例。
正是Singleton模式的引入,使得我们的WorldMgr类可以在平行世界的其他活动中被轻松调用(只需WorldMgr::getInstance() 即可返回它唯一的实例)。但这还不足以让时の魔导士彻底松一口气。作为一个议会,WorldMgr统管世界的方方面面,未免有点太至高无上了。所谓权利的完全集中可能会导致彻底的腐败,倘若魔兽世界中只有一头巨龙--死亡之翼,那么它的堕落将导致艾泽拉斯大陆不复存在。于是,时の魔导士需要一些个体来分摊这份权利,以使得他们之间相互制约、相互平衡。他顺势创造了Titan(泰坦),世界管理者议会的继承类:
掌管生命和死亡的 Iapetus
1 class Iapetus : public WorldMgr {
2 protected:
3 Iapetus();
4 }
掌管海洋的 Oceanus
1 class Oceanus : public WorldMgr {
2 protected:
3 Oceanus();
4 }
掌管天空的 Cronus
1 class Cronus : public WorldMgr {
2 protected:
3 Cronus();
4 }
掌管记忆的 Mnemosyne
1 class Mnemosyne : public WorldMgr {
2 protected:
3 Mnemosyne();
4 }
等等等等……
既然有这么多子类,如何在getInstance操作中去选择不同的泰坦们呢?这里,我们可以用环境变量的方法来选择。我们假定一个环境变量指定了Titan的名字:
复制代码
1 WorldMgr* WorldMgr::getInstance() {
2 if (_instance == 0) {
3 const char* name = getenv("TITAN");
4
5 if (strcmp(name, "Iapetus")) {
6 _instance = new Iapetus();
7 }
8 else if (strcmp(name, "Oceanus")) {
9 _instance = new Oceanus();
10 }
11 else if (strcmp(name, "Cronus")) {
12 _instance = new Cronus();
13 }
14 else if (strcmp(name, "Mnemosyne")) {
15 _instance = new Mnemosyne();
16 }
17 // ... other Titans
18 else {
19 _instance = new WorldMgr();
20 }
21 }
22 return _instance;
23 }
复制代码
不过这个方法的问题就在于,无论何时要定义一个新的Titan(即WorldMgr的子类),getInstance函数都必须被修改。于是我们可以采用另一种注册表的方法来动态链接。