設計模式(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子類的名字,把可能的修改限制到系統啓動過程的配置環境變量的語句。

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