
Singleton 大概是所有設計模式裡最好懂的一個,但也是最容易被用錯的一個。
先講結論
Singleton 就是「整個程式只准有一個實例」。聽起來很美好對吧?但它本質上就是一個全域變數的包裝紙,用錯了會讓你的程式碼變成一團義大利麵。該用的時候用、不該用的時候別硬用——這才是重點。
什麼時候你真的需要 Singleton?
你有沒有遇過這種狀況:兩個模組各自 new 了一個 ConfigManager,結果 A 改了設定、B 完全不知道?或是 DB connection 被重複建立了十幾條,伺服器直接跪了?
這些場景的共通點是:資源必須唯一、狀態必須一致。
classDiagram class Singleton { -static instance : Singleton -_config : Object -Singleton() +static getInstance() Singleton +getConfig(key) any +setConfig(key, value) void }
最經典的用法:Config Manager
全域設定這東西,你絕對不想讓它有兩份。一份就夠了,多了只會打架。
class ConfigManager {
constructor() {
this._config = {};
}
static getInstance() {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
getConfig(key) {
return this._config[key];
}
setConfig(key, value) {
this._config[key] = value;
}
}
const config = ConfigManager.getInstance();
config.setConfig('API_URL', 'https://api.example.com');
console.log(config.getConfig('API_URL')); // https://api.example.com進階一點:用 IIFE 做模組級 Singleton
如果你覺得上面那種寫法太 Java 味了,JavaScript 其實可以用閉包做得更乾淨。重點是把 instance 藏起來,外面根本摸不到。
const DatabaseConnection = (function() {
let instance;
function createInstance() {
let connection = null;
function connect() {
connection = { id: Math.random() };
console.log('建立新的資料庫連線');
}
return {
getConnection: function() {
if (!connection) connect();
return connection;
}
};
}
return {
getInstance: function() {
if (!instance) instance = createInstance();
return instance;
}
};
})();
const conn1 = DatabaseConnection.getInstance().getConnection();
const conn2 = DatabaseConnection.getInstance().getConnection();
console.log(conn1 === conn2); // true — 同一條連線那 Singleton 有什麼問題?
老實說,問題不少:
- 測試超痛苦:全域狀態意味著測試之間會互相污染。你的 test A 改了 config,test B 就爆了,還查不出原因
- 隱性依賴:程式碼裡到處都
getInstance(),但你看不出誰依賴誰。重構的時候就知道痛了 - 違反單一職責:一個 class 同時管「業務邏輯」和「確保自己只有一個」,這兩件事根本不相干
所以很多現代框架(Angular、NestJS)選擇用 DI(依賴注入) 來管理 singleton scope,而不是讓你自己寫 getInstance()。框架幫你保證只有一個,你專心寫業務邏輯就好。
什麼時候該考慮替代方案?
反問自己:「這個東西真的需要全域唯一嗎?還是我只是懶得傳參數?」
如果答案是後者——用 DI。如果答案是前者——確認你的 Singleton 是 thread-safe 的(在 Node.js 裡不太需要擔心,但 Java 就得用 double-checked locking)。
Singleton 是設計模式的 Hello World,但千萬別因為它簡單就到處亂用——你不會想在 code review 被問「為什麼這個也要 Singleton」的。
延伸閱讀
- Factory 模式 — 工廠模式常搭配 Singleton 使用
- Strategy 模式 — 策略模式可與 Singleton 結合管理全域策略切換
- Builder 模式 — 另一種創建型模式