話說一群土著人生活地球的某個不為人知的地方,他們過著和諧、快樂的日子。但是好景不長,一只從天上掉下來的玻璃瓶打破了這美好的時光。這只玻璃瓶用處非常廣泛,有的土著人用它來砸地瓜,有的用它來吹口哨,有的用它來擀蛇皮……,漸漸地大家干活、娛樂都離不開它了,於是產生了爭搶使用可樂瓶的情況,為了獲得原有的和諧氣氛,一個土著人決定扔掉這個瓶子……
劇情講完了,我們先為這個瓶子定義一個類:
class CGlassBottle
{
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
public:
static BOOL OnKnock();//砸
static BOOL OnBlow();//吹
static BOOL OnRoll();//擀
protected:
static BOOL s_bUsed;//是否正在被使用
};
我們定義一個土著人A類,假設他只用玻璃瓶砸地瓜:
class A//土著人A
{
public:
void UseBottle()
{
CGlassBottle::OnKnock();
};
};
我們再定義一個土著人B類,假設他只用玻璃瓶吹口哨:
class B//土著人B
{
public:
void UseBottle()
{
CGlassBottle::OnBlow();
};
};
現在我們定義土著人A、B類的對象,讓他們執行UseBottle:int main(int argc, char* argv[])
{
A a;
B b;
a.UseBottle();//執行Knock
b.UseBottle();//執行blow
return 0;
}
我們來執行一下,看看結果:
圖一 執行結果
執行結果跟我們預期的一樣,這就是傳說中的類單件,土著人A用它來砸地瓜,土著人B用它來吹口哨。我個人對類單件的理解是這樣的,類單件是對相似的函數塊、功能塊或其他模塊進行封裝,盡量不涉及類的屬性。像二進制,十進制,十六進制字符串進行相互轉換的函數,如:
class CNumFormat
{
private:
CNumFormat();
public:
static CString From2TO10(LPCTSTR lpszTxt2);
static CString From2TO16(LPCTSTR lpszTxt2);
static CString From10TO2(LPCTSTR lpszTxt2);
static CString From10TO16(LPCTSTR lpszTxt2);
static CString From16TO2(LPCTSTR lpszTxt2);
static CString From16TO10(LPCTSTR lpszTxt2);
};
在本例子中的CGlassBottle類,這樣用也是可以的,但我們還可以把它設計得更好。在《深入淺出設計模式》中是這樣定義單件模式的:確保一個類只有一個實例,並提供全局訪問點。下面我們根據該定義對CGlassBottle類進行改進:
class CGlassBottle
{
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
public:
bool OnKnock();//砸
bool OnBlow();//吹
bool OnRoll();//擀
public:
static CGlassBottle s_GlassBottle;//定義
protected:
bool m_bUsed;//是否正在被使用
};
對土著人A、B類進行相應的修改:
class A//土著人A
{
public:
void UseBottle()
{
CGlassBottle::s_GlassBottle.OnKnock();
};
};
class B//土著人B
{
public:
void UseBottle()
{
CGlassBottle::s_GlassBottle.OnBlow();
};
};
很多時候我們不希望s_GlassBottle暴露出來,而更希望通過函數調用來獲取,於是增加一個獲取s_GlassBottle的函數,同時把GlassBottle搞成私有靜態成員,具體如下:
class CGlassBottle
{
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
public:
bool OnKnock();//砸
bool OnBlow();//吹
bool OnRoll();//擀
public:
static CGlassBottle* GetInstance()
{
return &s_GlassBottle;
}
private:
static CGlassBottle s_GlassBottle;//定義
protected:
bool m_bUsed;//是否正在被使用
};
土著人 A、B類在UseBottle()時進行如下修改:class A//土著人A
{
public:
void UseBottle()
{
CGlassBottle::GetInstance()->OnKnock();
};
};
class B//土著人B
{
public:
void UseBottle()
{
CGlassBottle::GetInstance()->OnBlow();
};
};
可能許多朋友都聽說過延遲實例化這個概念,那我們這裡也玩玩這個概念:
class CGlassBottle
{
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
public:
bool OnKnock();//砸
bool OnBlow();//吹
bool OnRoll();//擀
public:
static CGlassBottle* GetInstance()
{
static CGlassBottle s_GlassBottle;//定義
return &s_GlassBottle;
}
protected:
bool m_bUsed;//是否正在被使用
};
其實我也偏好這種用法,不知道在多線程下面會不會有問題(我只在單線程下玩,所以還沒有發現過問題)。但是你會發現在調用GetInstance()的時候必須要在前面加上CGlassBottle::,在調用時像下面這樣:
CGlassBottle::GetInstance()->OnKnock();
這是不是會讓人覺得啰嗦呢,至少對我來說很啰嗦,但我們還可以像下面這麼處理:
#define GetGlassBottle() CGlassBottle::GetInstance()
用的時候就這麼用“GetGlassBottle()->Knock();”,當然還可以對CGlassBottle進行改進,不知道這叫不叫改進:
class CGlassBottle
{
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
public:
bool OnKnock();//砸
bool OnBlow();//吹
bool OnRoll();//擀
private:
friend CGlassBottle* GetGlassBottle();
static CGlassBottle* GetInstance()
{
static CGlassBottle s_GlassBottle;//定義
return &s_GlassBottle;
}
protected:
bool m_bUsed;//是否正在被使用
};
inline CGlassBottle* GetGlassBottle()
{
return CGlassBottle::GetInstance();
}
現在可以這樣用了:
GetGlassBottle()->OnKnock();
這樣看起來是不是舒服多了。
我這個人比較懶,一直喜歡像下面這樣用,因為一直沒有出現過問題,所以用得挺爽:
class CGlassBottle
{
private:
CGlassBottle();
public:
virtual ~CGlassBottle();
public:
bool OnKnock();//砸
bool OnBlow();//吹
bool OnRoll();//擀
private:
friend CGlassBottle* GetGlassBottle();
protected:
bool m_bUsed;//是否正在被使用
};
inline CGlassBottle* GetGlassBottle()
{
static CGlassBottle s_GlassBottle;//定義
return &s_GlassBottle;
}
文章到此結束,如有不妥之處,請來信告知。
參考文獻:
1.《設計模式-可復用面向對象軟件的基礎》-- 李英軍等譯
2.《深入淺出設計模式》-- Eric Freeman,Elisabeth Freeman,^Kathy Sierra & Bert Bates著
本文配套源碼