实现(续)

 

2) 创建Singleton类的子类

主要问题与其说是定义子类不如说是建立它的唯一实例,这样客户就可以使用它。事实上,指向单件实例的变量必须用子类的实例进行初始化。最简单的技术是在Singleton的Instance操作中决定你想使用的是哪一个单件。代码示例一节中的一个例子说明了如何用环境变量实现这一技术。

 

《设计模式》中没有提供示例代码,作者认为大致应该如下:

 

Aha!设计模式(40)-单例(6)_示例代码

有两点需要说明:

  1. 代码中使用GetClassType来决定具体使用那个类的实例,也可以使用其他的方法,甚至宏定义。具体怎么做决定于实际的需求。
  2. 上述代码在基类的Cpp文件中引入了派生类的头文件。这是一个问题,但为了实现简单,恐怕也只能这样了。

 

另一个选择Singleton的子类的方法是将Instance的实现从父类(即MazeFactory)中分离出来并将它放入子类。这就允许C++程序员在链接时刻决定单件的类(即通过链入一个包含不同实现的对象文件),但对单件的客户则隐蔽这一点。

 

《设计模式》中同样没有提供示例代码,以下是作者根据自己的理解提供的例子。

 

Aha!设计模式(40)-单例(6)_子类_02

在SubClass21的cpp文件中实现了基类Singleton2的Instance类方法。也可同样的方式实现SubClass22等。只要能够在编译时控制只用一个派生类参加编译即可。

 

链接的方法在链接时刻确定了单件类的选择,这使得难以在运行时刻选择单件类。使用条件语句来决定子类更加灵活一些,但这硬性限定(hard-wire)了可能的Singleton类的集合。这两种方法不是在所有的情况都足够灵活的。

这两种方法都能解决问题,但方法都是同样的简单粗暴。
一个更灵活的方法是使用一个单件注册表(registry of singleton)。可能的Sing leton类的集合不是由Instance定义的,Singleton类可以根据名字在一个众所周知的注册表中注册它们的单件实例。
这个注册表在字符串名字和单件之间建立映射。当Instance需要一个单件时,它参考注册表,根据名字请求单件。
注册表查询相应的单件(如果存在的话)并返回它。这个方法使得Instance不再需要知道所有可能的Singleton类或实例。它所需要的只是所有Singleton类的一个公共的接口,该接口包括了对注册表的操作:

 

Aha!设计模式(40)-单例(6)_设计模_03

Aha!设计模式(40)-单例(6)_设计模式_04

Register以给定的名字注册Singleton实例。为保证注册表简单,我们将让它存储一列NameSingletonPair对象。每个NameSingletonPair将一个名字映射到一个单件。 Lookup操作根据给定单件的名字进行查找。我们假定一个环境变量指定了所需要的单件的名字。

 

Aha!设计模式(40)-单例(6)_示例代码_05

Singleton类在何处注册它们自己?一种可能是在它们的构造器中。例如,MySingleton子类可以像下面这样做:

 

Aha!设计模式(40)-单例(6)_示例代码_06

当然,除非实例化类否则这个构造器不会被调用,这正反映了Singleton模式试图解决的问题!在C++中我们可以定义MySingleton的一个静态实例来避免这个问题。例如,我们可以在包含MySingleton实现的文件中定义:
static MySingleton theSingleton;
Singleton类不再负责创建单件。它的主要职责是使得供选择的单件对象在系统中可以被访问。静态对象方法还是有一个潜在的缺点 — 也就是所有可能的Singl eton子类的实例都必须被创建,否则它们不会被注册。

 

作者观点

 

Singleton模式最主要的是要保证只能使用同一个实例,这是对实例的使用者而言的。只要能满足这个要求,是否要保证真正只有一个实例被创建可能不是那么重要。

 

注:

 

本文中蓝色粗体文字都引自《设计模式》一书。

 

觉得本文有帮助?请分享给更多人。

阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】

Aha!设计模式(40)-单例(6)_子类_07