分類

建立型模式。

這個模式在解決什麼

單例模式想處理的是:某個類別在系統中只應該有一個實例,而且大家都要透過同一個入口來取得它。

常見原因是這個物件代表某種全域協調者,例如設定管理、流程控制器或共享服務容器。

核心做法

做法通常包含三件事:

  • 把建構子設成私有,避免外部直接建立
  • 由類別自己保管唯一實例
  • 對外提供固定入口讓其他程式取得該實例

什麼時候適合用

如果某個物件在整個系統裡只該存在一份,而且多份實例會造成狀態衝突,就可以考慮單例模式。

但這個模式也很容易被濫用。只要不是明確的全域唯一職責,就不該因為「取用方便」而直接做成單例,否則測試、依賴管理和生命週期控制都會變差。

例子

像遊戲中的總流程管理器、全域設定管理器,或某些確定只該有一份的服務控制器,都可能採用單例模式。前提是你真的接受它是全域共享狀態,而不是把一般物件硬塞成全域入口。

類圖

示例代碼

延遲實例化版本:

public class Singleton
{
    private static Singleton instance;

    private Singleton() {}

    public static Singleton GetInstance()
    {
        if (instance == null)
        {
            instance = new Singleton();
        }

        return instance;
    }
}

急切實例化版本:

public class Singleton
{
    private static readonly Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton GetInstance()
    {
        return instance;
    }
}

使用時要注意

  • 延遲實例化比較省資源,但多執行緒情境要特別留意同步問題
  • 急切實例化比較單純,但如果實例很重、又不一定會用到,就可能浪費資源
  • 單例常常會把依賴藏成全域狀態,長期來看要小心測試成本

這一篇先記住的重點

  • 單例模式保證唯一實例,並提供全域存取入口
  • 它適合少數真正「只能有一份」的對象
  • 方便不代表應該濫用,尤其在大型專案中更要節制

參考書目

《Head First Design Patterns》Eric Freeman, Elisabeth Robson, Kathy Sierra, Bert Bates