首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

單例模式的趣味演繹 — 土著人的可樂瓶

     編輯:關於VC++

故事開場

某不知名處的一群土著人過著和諧快樂的日子。直到一個玻璃瓶從天而降打破平靜。這瓶子用途極廣:有人砸地瓜、有人吹口哨、有人擀蛇皮……大夥兒離不開它,搶著用。為恢復和諧,一位土著決定把瓶子扔掉……

第一版:類單例

定義瓶子類,所有方法皆為 static,不涉及屬性:

class CGlassBottle {
private:
    CGlassBottle();
public:
    virtual ~CGlassBottle();
    static BOOL OnKnock(); // 砸
    static BOOL OnBlow();  // 吹
    static BOOL OnRoll();  // 擀
protected:
    static BOOL s_bUsed;
};

土著 A 只砸地瓜,土著 B 只吹口哨:

class A { public: void UseBottle() { CGlassBottle::OnKnock(); } };
class B { public: void UseBottle() { CGlassBottle::OnBlow(); } };

執行結果符合預期,這就是「類單例」——封裝相似功能塊,盡量不涉及屬性。如進制轉換工具類:

class CNumFormat {
private: CNumFormat();
public:
    static CString From2TO10(LPCTSTR);
    static CString From2TO16(LPCTSTR);
    ...

第二版:標準單例模式

《深入淺出設計模式》定義:確保一個類別只有一個實例,並提供全域存取點。改進瓶子類:

class CGlassBottle {
private:
    CGlassBottle();
public:
    virtual ~CGlassBottle();
    bool OnKnock(); bool OnBlow(); bool OnRoll();
public:
    static CGlassBottle s_GlassBottle; // 靜態實例
protected:
    bool m_bUsed;
};

土著呼叫改為 CGlassBottle::s_GlassBottle.OnKnock()。

第三版:私有靜態實例 + 存取函數

不想暴露實例,改用 GetInstance():

class CGlassBottle {
private:
    CGlassBottle();
    static CGlassBottle s_GlassBottle;
public:
    virtual ~CGlassBottle();
    bool OnKnock(); bool OnBlow(); bool OnRoll();
    static CGlassBottle* GetInstance() { return &s_GlassBottle; }
protected:
    bool m_bUsed;
};

呼叫:CGlassBottle::GetInstance()->OnKnock()。

第四版:延遲實例化 (Lazy Initialization)

利用函數靜態區域變數,首次呼叫才建構:

static CGlassBottle* GetInstance() {
    static CGlassBottle s_GlassBottle; // 延遲建構
    return &s_GlassBottle;
}

筆者偏好此寫法,單執行緒下無問題,多執行緒安全性視編譯器而定 (C++11 後保證 thread-safe)。

第五版:宏簡化呼叫

覺得 CGlassBottle::GetInstance()->OnKnock() 囉嗦?定義宏:

#define GetGlassBottle() CGlassBottle::GetInstance()
GetGlassBottle()->OnKnock();

第六版:友元函數 + 全域內聯函數

將 GetInstance 設為私有,授權友元函數 GetGlassBottle():

class CGlassBottle {
private:
    CGlassBottle();
    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();
    friend CGlassBottle* GetGlassBottle();
protected:
    bool m_bUsed;
public:
    bool OnKnock(); bool OnBlow(); bool OnRoll();
    virtual ~CGlassBottle();
};
inline CGlassBottle* GetGlassBottle() {
    static CGlassBottle s_GlassBottle;
    return &s_GlassBottle;
}

用法同前,簡潔且單執行緒下從未出問題。

參考文獻

  1. 《設計模式——可復用面向物件軟體基礎》 李英軍等譯
  2. 《深入淺出設計模式》 Eric Freeman 等著
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved