对于有些类,它们只需要一个实例,比如有个资源管理类,它负责管理一个系统中的资源,这样的类的实例整个系统仅仅需要一个。如果创建了多份就会引起严重的逻辑错误。为了避免这种错误,Agile中提到了2种解决方法,一个是Singleton模式,一个是Monstate模式,下面就是对这两种模式的分析。
Singleton模式
Singleton模式的结构与基本实现并不困难,它的结构图如下:
从结构图中我们可以看到Singleton有一个protected的构造函数Singleton(),这个限制使得外界不能直接实例化这个类。另外它有一个静态的Singleton变量uniqueInstance。这个变量就是这个类的唯一实例,我们可以通过静态方法Instance来得到它。从结构图中Instance()方法的注释中我们看到的是Singleton模式的懒汉式实现。它在第一次被引用的时候才将自己实例化。与其相对的就是在Java等语言中有一种饿汉式的实现方法。它就是在类的声明中直接初始化uniqueInstance,这样在类被加载的时候uniqueInstance就会被初始化。这样做单从资源利用效率上来看是不如懒汉式的,但是它速度快,而且不用担心同时有几个线程都进入if(uniquInstance == null)中用造成的多个实例被创建的问题。但是在C++中无法实现懒汉式的Singleton,因为静态初始化在C++中没有固定的顺序,因而变量初始化和类加载顺序不确定。要避免多线程的问题可以通过双重检测来完成。
由于static成员不能被继承。所以Singleton的一个难点就是子类的创建,这里有2种方法:
1. 利用简单工厂模式,在BaseInstance类中的Instance操作中用if语句决定要实例哪个具体的子类。但这就表明BaseInstance类必须知道它所有的子类。
2. 把Instance方法从BaseInstance移到它的子类中。这就允许C++程序员在链接时刻决定单件的类(即通过链入一个包含不同实现的对象文件),但对客户隐藏这点。不过这就使得难以在运行时刻选择单例类。
一个更灵活的方法是使用一个单例注册表(registry of singleton)。当要获得某个Singleton就可以查询这个注册表。如果已经存在就提取这个实例;如果不存在就注册一个。
OGRE中实现单例的方法是使用模板类:
template <typename T> class Singleton
{
protected:
static T* ms_Singleton;
public:
Singleton( void )
{
assert( !ms_Singleton );//与if( !ms_Singleton)的区别
ms_Singleton = static_cast< T* >( this );
}
~Singleton( void )
{ assert( ms_Singleton ); ms_Singleton = 0; }
static T& getSingleton( void )
{ assert( ms_Singleton ); return ( *ms_Singleton ); }
static T* getSingletonPtr( void )
{ return ms_Singleton; }
};
其它的类继承于它:
class MyClass : public Singleton< MyClass >
{
.....
static MyClass & getSingleton(void);
static MyClass * getSingletonPtr(void);
};
这个方法使得如果你在程序中实例化了两个实例就会提示然后退出程序。它是Singleton的另一种使用方式,面向程序员自己而不是面向客户的。
MONOSTATE模式
Monostate是另一种获取对象单一性的方法。它的原理很简单,就是一个Monostate的所有对象共享相同的变量。要做到这一点只要把所有的变量都变成静态变量即可。
Monostate模式的优点:
1. 透明性 用户不必知道使用的对象是Monostate。
2. 可派生性 构造子是公有的,所以可以派生。它的所有派生类都共
享相同的静态变量。
3. 多态性 由于Monostate的方法不是静态的,所以可以在派生类中
覆写它们,因此,不同的派生类可以基于同样的静态变
量表现出不同的行为。
Monostate模式的缺点:
1. 不可转换性: 不能透过派生把常规类转换成Monostate。
2. 效率问题: 因为Monostate是真正的对象,所以会导致许多的创
建和摧毁的开销
3. 内存占用: 即使从未使用Monostate,它的变量也要占据内存空
间。
4.平台局限性: Monostate不能跨越多个平台。