單例模式 (Singleton Pattern)
為什麼需要 Singleton?
在開發大型系統時,我們經常會遇到這樣的需求:某些資源應該在整個應用程式中保持唯一性。
舉個實際例子,假設你在開發一個分散式系統,多個模組都需要記錄 Log。如果每個模組都各自創建 Logger 實例:
- Log 訊息會分散在不同的物件中,難以統一管理和追蹤
- 無法維持一致的設定(例如 Log 等級、輸出格式)
- 記憶體中會存在多個相同功能的物件,造成資源浪費
這時候我們希望有一個全域的 Logger 管理者,所有模組都向它發送 Log 訊息。
Singleton 的核心思想:確保一個類別在整個系統中只有一個實例,並提供全域的存取點。
沒有使用 Singleton 的情況
來看看傳統的實作方式會產生什麼問題:
1 | class Logger: |
執行結果:
1 | [LOG] A 模組初始化完成 |
可以看到,每次實例化都會產生不同的物件,這違背了我們想要統一管理的初衷。
使用 Singleton Pattern
接下來實作 Singleton 版本:
1 | class SingletonLogger: |
執行結果:
1 | 建立 SingletonLogger 實例 |
可以看到,不論在哪個模組中實例化,都會得到相同的物件參考。
實作細節說明
在這個實作中,我們重寫了 __new__
方法來控制物件的創建過程:
- 使用類別變數
_instance
來儲存唯一的實例 - 在
__new__
中檢查是否已經存在實例 - 如果不存在,則創建新實例;如果存在,則回傳現有實例
這種做法確保了無論呼叫多少次建構子,都只會有一個實例存在。
適用場景
Singleton 適合用於以下情況:
建議使用:
- 日誌系統(Logger)
- 設定檔管理器(Configuration Manager)
- 資料庫連線池(Database Connection Pool)
- 快取管理器(Cache Manager)
- 執行緒池(Thread Pool)
不建議使用:
- 一般業務邏輯物件
- 需要多個不同狀態的物件
- 會影響單元測試的情況
注意事項
使用 Singleton 時需要注意:
- 執行緒安全性:在多執行緒環境中,需要考慮同步問題
- 測試困難:Singleton 會在測試之間保持狀態,可能影響測試的獨立性
- 過度使用:不要把所有東西都做成 Singleton,這會增加系統耦合度
Singleton 是一個有用的設計模式,但要在適當的場景下使用,避免為了使用設計模式而使用設計模式。