意圖(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-80 的
ChangeSet current——程式碼變更集合的唯一實例 - Smalltalk-80 的 metaclass 機制——每個 metaclass 恰好有一個實例
- InterViews 框架的 Session 與 WidgetKit 類別——Session 管理事件迴圈與顯示器設定,WidgetKit 是 UI widget 的 Abstract Factory,兩者都是 Singleton
相關模式(Related Patterns)#
- 許多模式可透過 Singleton 實作,包括 Abstract Factory、Builder 與 Prototype