保證壹個類僅有壹個實例,並提供壹個訪問它的全局訪問點。
2. 動機
對壹些類來說,只有壹個實例是很重要的。雖然系統中可以有許多打印機,但卻只應該有壹個打印假脫機( printer spooler),只應該有壹個文件系統和壹個窗口管理器。壹個數字濾波器只能有壹個A / D轉換器。壹個會計系統只能專用於壹個公司。
我們怎麽樣才能保證壹個類只有壹個實例並且這個實例易於被訪問呢?壹個全局變量使得壹個對象可以被訪問,但它不能防止妳實例化多個對象。壹個更好的辦法是,讓類自身負責保存它的唯壹實例。這個類可以保證沒有其他實例可以被創建(通過截取創建新對象的請求),並且它可以提供壹個訪問該實例的方法。這就是Singleton模式。
3. 適用性
在下面的情況下可以使用Singleton模式. 當類只能有壹個實例而且客戶可以從壹個眾所周知的訪問點訪問它時。 當這個唯壹實例應該是通過子類化可擴展的,並且客戶應該無需更改代碼就能使用壹個擴展的實例時。
4. 結構
5. 參與者
Singleton
定義壹個GetInstance操作,允許客戶訪問它的唯壹實例。GetInstance是壹個類操作(即Smalltalk中的壹個類方法和C++中的壹個靜態成員函數)。可能負責創建它自己的唯壹實例。
6. 協作
客戶只能通過Singleton的GetInstance操作訪問壹個Singleton的實例。
7. 效果
Singleton模式有許多優點:
1) 對唯壹實例的受控訪問因為Singleton類封裝它的唯壹實例,所以它可以嚴格的控制客戶怎樣以及何時訪問它。
2) 縮小名空間Singleton模式是對全局變量的壹種改進。它避免了那些存儲唯壹實例的全局變量汙染名空間。
3) 允許對操作和表示的精化Singleton類可以有子類,而且用這個擴展類的實例來配置壹個應用是很容易的。妳可以用妳所需要的類的實例在運行時刻配置應用。
4) 允許可變數目的實例這個模式使得妳易於改變妳的想法,並允許Singleton類的多個實例。此外,妳可以用相同的方法來控制應用所使用的實例的數目。只有允許訪問Singleton實例的操作需要改變。
5) 比類操作更靈活另壹種封裝單件功能的方式是使用類操作(即C++中的靜態成員函數或者是Smalltalk中的類方法)。但這兩種語言技術都難以改變設計以允許壹個類有多個實例。
此外,C++中的靜態成員函數不是虛函數,因此子類不能多態的重定義它們。
8. 實現
class Singleton
{
static std::auto_ptr<Singleton> m_pInstance;
protected:
//prevent user making our any instance by manually
//構造函數是保護類型的。
Singleton(){}
public:
~Singleton(){}
//Return this singleton class' instance pointer
static Singleton* Instance()
{
if(!m_pInstance.get())
{
m_pInstance = std::auto_ptr<Singleton>(new Singleton());
}
return m_pInstance.get();
}
};
怎樣來使用它呢?不要試圖從這個類派生妳的單件子類,那樣的結果是不妥當的,如果妳需要多個單件子類,還是使用下面的宏定義更為妥當:
#define DEFINE_SINGLETON(cls)\
private:\
static std::auto_ptr<cls> m_pInstance;\
protected:\
cls(){}\
public:\
~cls(){}\
static cls* Instance(){\
if(!m_pInstance.get()){\
m_pInstance = std::auto_ptr<cls>(new cls());\
}\
return m_pInstance.get();\
}
#define IMPLEMENT_SINGLETON(cls) \
std::auto_ptr<cls> cls::m_pInstance(NULL);
假定妳需要實現壹個單件類YY,這樣書寫:
class YY
{
DEFINE_SINGLETON(YY);
public:
//your interfaces here...
};
在cpp文件中,書寫:
IMPLEMENT_SINGLETON(YY);
需要引入這個類的實例的時候,使用這樣的語句:
YY* pYY = YY::Instance();
這,就是全部。
如果需要定義其他的單件類,重復上面的定義,就可以了。
當想集中管理壹個應用程序所需的所有配置時,可以聲明壹個CToolsOptions的類,其中包含配置屬性集合。對於這個類的實例,顯然是壹個實例就夠了;當編寫繪圖程序時,考慮繪制矩形,圓形等分別使用CGraphTool派生的工具類,每個派生類負責處理具體的繪制動作和相關的UI相應邏輯。這些工具類典型的在被用戶選擇工具欄的圖元按鈕時被選中。依照這種模式,妳應該對所有的圖元工具從事註冊工作,使得繪圖程序了解運行時刻可以使用那些圖元工具。同樣的,負責管理註冊信息的這個管理器也只需要壹個實例就行了。