意圖(Intent)#

確保一個類別只有一個實例,並提供一個全域存取點。

動機(Motivation)#

某些類別必須恰好只有一個實例——例如系統中只應有一個 printer spooler、一個 file system、一個 window manager。全域變數雖然能讓物件可存取,卻無法防止建立多個實例。

更好的做法是讓類別自身負責追蹤其唯一實例:攔截建立新實例的請求,確保不會建立第二個實例,並提供存取該實例的方式。這就是 Singleton 模式。

適用場景(Applicability)#

  • 類別必須恰好只有一個實例,且必須從一個眾所周知的存取點取得
  • 唯一實例需要能透過子類別化來擴展,且 client 不需修改程式碼就能使用擴展後的實例

結構(Structure)#

Singleton 類別定義一個 Instance 類別操作(class method / static method),讓 client 存取唯一的實例。建構子設為 protected 或 private,防止外部直接實體化。

classDiagram
    class Singleton {
        -static uniqueInstance
        -singletonData
        +static Instance()
        +SingletonOperation()
        +GetSingletonData()
    }

參與者(Participants)#

參與者範例職責
Singleton-定義 Instance 操作,讓 client 存取唯一實例(Smalltalk 的 class method / C++ 的 static member function);可能負責建立自己的唯一實例

協作方式(Collaborations)#

  • Client 僅透過 Singleton 的 Instance 操作來存取實例

優缺點(Consequences)#

  • 控制對唯一實例的存取 —— Singleton 類別封裝其唯一實例,能嚴格控制 client 的存取方式與時機
  • 減少命名空間污染 —— 比全域變數更好,避免儲存唯一實例的全域變數污染命名空間
  • 允許精化操作與表示 —— Singleton 類別可被子類別化,應用程式可在執行時期配置所需的擴展類別實例
  • 允許可變數量的實例 —— 日後可輕鬆改為允許多個實例,只需修改控制存取的操作即可
  • 比類別操作更有彈性 —— static member function 或 class method 無法被子類別多型覆寫,而 Singleton 實例方法可以

Singleton 不只是全域變數的替代品——它提供了受控的存取延遲初始化、以及可子類別化的能力,這些都是全域變數做不到的。

實作要點(Implementation)#

  • 確保唯一實例

    • 將建構子設為 protected / private
    • 提供 static Instance 方法,以 lazy initialization 在首次存取時建立實例
    • 不要依賴全域或 static 物件的自動初始化——無法保證只有一個 static 宣告,也無法控制跨翻譯單元的初始化順序
  • 子類別化 Singleton

    • 最簡單的方法:在 Instance 中用條件判斷(如環境變數)決定要建立哪個子類別
    • 另一做法:將 Instance 的實作放在子類別中,在 link-time 決定
    • 最有彈性的方法:Singleton Registry(註冊表)——各 Singleton 子類別以名稱向一個共用 registry 註冊,Instance 依名稱查詢並回傳對應的實例

在 C++ 中不要將 Singleton 實作為全域或 static 物件。原因有三:(1) 無法保證只有一個 static 宣告 (2) 可能在 static 初始化時缺乏必要資訊 (3) C++ 不定義跨翻譯單元的 static 物件初始化順序。

  • Registry 方式的取捨 —— Singleton 子類別需要在某處觸發註冊。若放在建構子中,就回到了「誰來建立第一個實例」的問題。C++ 中可用檔案層級的 static 實例來觸發,但代價是所有可能的子類別實例都會被建立,無論是否使用

若 Singleton 只有一個具體類別且不需子類別化,最簡單的實作就是 static Instance 方法 + protected 建構子 + lazy initialization。保持簡單即可。

已知應用(Known Uses)#

  • Smalltalk-80ChangeSet current——程式碼變更集合的唯一實例
  • Smalltalk-80 的 metaclass 機制——每個 metaclass 恰好有一個實例
  • InterViews 框架的 Session 與 WidgetKit 類別——Session 管理事件迴圈與顯示器設定,WidgetKit 是 UI widget 的 Abstract Factory,兩者都是 Singleton

相關模式(Related Patterns)#

  • 許多模式可透過 Singleton 實作,包括 Abstract FactoryBuilderPrototype