程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 單件模式之土著人的可樂瓶

單件模式之土著人的可樂瓶

編輯:關於VC++

話說一群土著人生活地球的某個不為人知的地方,他們過著和諧、快樂的日子。但是好景不長,一只從天上掉下來的玻璃瓶打破了這美好的時光。這只玻璃瓶用處非常廣泛,有的土著人用它來砸地瓜,有的用它來吹口哨,有的用它來擀蛇皮……,漸漸地大家干活、娛樂都離不開它了,於是產生了爭搶使用可樂瓶的情況,為了獲得原有的和諧氣氛,一個土著人決定扔掉這個瓶子……

劇情講完了,我們先為這個瓶子定義一個類:

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著

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved