设计模式(5) - 单件模式

问题描述

某些类型(例如前面介绍的工厂类)在一个系统中只能出现一份实例。《More Effective C++》条款26详细的讨论一些可能的方法。那么,单件模式给我们带来了什么呢?

单件模式

如图所示,单件模式提供的解决方案是:让类自己保存自己的一份实例(m_instance),提供一个访问该实例的方法(Instance()),并限制其他途径来创建另外一个实例。


讨论

1)。线程安全性:  由于m_instance是静态成员变量,在多线程环境下,如果两个线程同时判断“if(m_instance == 0)”,那么两个线程都可能满足条件,从而创建出两份实例。然而m_instance只可能保留某个线程的创建结果,显然这里出现了内存泄露。更奇怪的事情是:某个线程两次调用Instance()方法返回的实例指针可能完全不同!我们不能接受内存泄露,更不能忍受这种不确定性,因此需要确保Instance()方法被多线程互斥地访问。

2)。如何创建Singletone的子类,并且让Instance()返回子类的实例对象? 下面的代码似乎可以解决这个问题,但是很明显的是:添加任何一个Singleton的子类例如SingletonChildB,Instance()函数都必须修改,这违反了Open/Close原则。一个改进的方式是:在Singleton中实现一个单件注册表,Instance()函数通过查表来返回一个已经注册了的单件。

class SingletonChildA;
class Singleton
{
public:
    static Singleton* Instance(const char* child_name)
    {
        if(m_instance == 0)
        {
            if(strcmp(child_name, "SingletonChildA") == 0)
            {
                m_instance = new SingletonChildA();
            }
            else if(...)
            {
            }
            else
            {
                m_instance = new Singleton();
            }
        }
        return m_instance;
    }
private:
    static Singleton* m_instance;
};
单件注册表的支持代码如下:

class Singleton
{
public:
    static void Register(const char* child_name, Singleton*);
    static Singleton* Instance()
    {
        const char* singletonName = getenv("SINGLETON");
        return m_maps[singletonName];
    }
private:
    static map<char*, Singleton*> m_maps;
};

class SingletoneChildA : public Singletone
{
public:
    SingletoneChildA()
    {
        Singleton::Register("SingletonChildA", this);
    }
....
};
static SingletonChildA theSingletonChildA;

上述代码中值得一提的是:Instance()接口不能通过参数传递Singleton子类的名字。想象一下,如果在任何调用Instance()的地方都传递一个名字,一旦需求发生变化,需要修改另外一个子类的名字的时候,代码需要到处修改!考虑到某个时刻特定类型的Singleton实例只需要一个,通过一个环境变量来配置Singleton子类的名字,把可能的修改限制到系统启动过程的配置环境变量的语句。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章