# 確保對象的唯一性——單例模式 (一)
3.1 單例模式的動機
對于一個軟件系統的某些類而言,我們無須創建多個實例。舉個大家都熟知的例子——Windows任務管理器,如圖3-1所示,我們可以做一個這樣的嘗試,在Windows的“任務欄”的右鍵彈出菜單上多次點擊“啟動任務管理器”,看能否打開多個任務管理器窗口?如果你的桌面出現多個任務管理器,我請你吃飯,微笑(注:電腦中毒或私自修改Windows內核者除外)。通常情況下,無論我們啟動任務管理多少次,Windows系統始終只能彈出一個任務管理器窗口,也就是說在一個Windows系統中,任務管理器存在唯一性。為什么要這樣設計呢?我們可以從以下兩個方面來分析:其一,如果能彈出多個窗口,且這些窗口的內容完全一致,全部是重復對象,這勢必會浪費系統資源,任務管理器需要獲取系統運行時的諸多信息,這些信息的獲取需要消耗一定的系統資源,包括CPU資源及內存資源等,浪費是可恥的,而且根本沒有必要顯示多個內容完全相同的窗口;其二,如果彈出的多個窗口內容不一致,問題就更加嚴重了,這意味著在某一瞬間系統資源使用情況和進程、服務等信息存在多個狀態,例如任務管理器窗口A顯示“CPU使用率”為10%,窗口B顯示“CPU使用率”為15%,到底哪個才是真實的呢?這純屬“調戲”用戶,偷笑,給用戶帶來誤解,更不可取。由此可見,確保Windows任務管理器在系統中有且僅有一個非常重要。

圖3-1 Windows任務管理器
回到實際開發中,我們也經常遇到類似的情況,為了節約系統資源,有時需要確保系統中某個類只有唯一一個實例,當這個唯一實例創建成功之后,我們無法再創建一個同類型的其他對象,所有的操作都只能基于這個唯一實例。為了確保對象的唯一性,我們可以通過單例模式來實現,這就是單例模式的動機所在。
3.2 單例模式概述
下面我們來模擬實現Windows任務管理器,假設任務管理器的類名為TaskManager,在TaskManager類中包含了大量的成員方法,例如構造函數TaskManager(),顯示進程的方法displayProcesses(),顯示服務的方法displayServices()等,該類的示意代碼如下:
```
class TaskManager
{
public TaskManager() {……} //初始化窗口
public void displayProcesses() {……} //顯示進程
public void displayServices() {……} //顯示服務
……
}
```
為了實現Windows任務管理器的唯一性,我們通過如下三步來對該類進行重構:
(1) 由于每次使用new關鍵字來實例化TaskManager類時都將產生一個新對象,為了確保TaskManager實例的唯一性,我們需要禁止類的外部直接使用new來創建對象,因此需要將TaskManager的構造函數的可見性改為private,如下代碼所示:
```
private TaskManager() {……}
```
(2) 將構造函數改為private修飾后該如何創建對象呢?不要著急,雖然類的外部無法再使用new來創建對象,但是在TaskManager的內部還是可以創建的,可見性只對類外有效。因此,我們可以在TaskManager中創建并保存這個唯一實例。為了讓外界可以訪問這個唯一實例,需要在TaskManager中定義一個靜態的TaskManager類型的私有成員變量,如下代碼所示:
```
private static TaskManager tm = null;
```
(3) 為了保證成員變量的封裝性,我們將TaskManager類型的tm對象的可見性設置為private,但外界該如何使用該成員變量并何時實例化該成員變量呢?答案是增加一個公有的靜態方法,如下代碼所示:
```
public static TaskManager getInstance()
{
if (tm == null)
{
tm = new TaskManager();
}
return tm;
}
```
在getInstance()方法中首先判斷tm對象是否存在,如果不存在(即tm == null),則使用new關鍵字創建一個新的TaskManager類型的tm對象,再返回新創建的tm對象;否則直接返回已有的tm對象。
需要注意的是getInstance()方法的修飾符,首先它應該是一個public方法,以便供外界其他對象使用,其次它使用了static關鍵字,即它是一個靜態方法,在類外可以直接通過類名來訪問,而無須創建TaskManager對象,事實上在類外也無法創建TaskManager對象,因為構造函數是私有的。
思考
為什么要將成員變量tm定義為靜態變量?
通過以上三個步驟,我們完成了一個最簡單的單例類的設計,其完整代碼如下:
```
class TaskManager
{
private static TaskManager tm = null;
private TaskManager() {……} //初始化窗口
public void displayProcesses() {……} //顯示進程
public void displayServices() {……} //顯示服務
public static TaskManager getInstance()
{
if (tm == null)
{
tm = new TaskManager();
}
return tm;
}
……
}
```
在類外我們無法直接創建新的TaskManager對象,但可以通過代碼TaskManager.getInstance()來訪問實例對象,第一次調用getInstance()方法時將創建唯一實例,再次調用時將返回第一次創建的實例,從而確保實例對象的唯一性。
上述代碼也是單例模式的一種最典型實現方式,有了以上基礎,理解單例模式的定義和結構就非常容易了。單例模式定義如下:
單例模式(Singleton Pattern):確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。單例模式是一種對象創建型模式。
單例模式有三個要點:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
單例模式是結構最簡單的設計模式一,在它的核心結構中只包含一個被稱為單例類的特殊類。單例模式結構如圖3-2所示:

單例模式結構圖中只包含一個單例角色:
● Singleton(單例):在單例類的內部實現只生成一個實例,同時它提供一個靜態的getInstance()工廠方法,讓客戶可以訪問它的唯一實例;為了防止在外部對其實例化,將其構造函數設計為私有;在單例類內部定義了一個Singleton類型的靜態對象,作為外部共享的唯一實例。
- Introduction
- 基礎知識
- 設計模式概述
- 從招式與內功談起——設計模式概述(一)
- 從招式與內功談起——設計模式概述(二)
- 從招式與內功談起——設計模式概述(三)
- 面向對象設計原則
- 面向對象設計原則之單一職責原則
- 面向對象設計原則之開閉原則
- 面向對象設計原則之里氏代換原則
- 面向對象設計原則之依賴倒轉原則
- 面向對象設計原則之接口隔離原則
- 面向對象設計原則之合成復用原則
- 面向對象設計原則之迪米特法則
- 六個創建型模式
- 簡單工廠模式-Simple Factory Pattern
- 工廠三兄弟之簡單工廠模式(一)
- 工廠三兄弟之簡單工廠模式(二)
- 工廠三兄弟之簡單工廠模式(三)
- 工廠三兄弟之簡單工廠模式(四)
- 工廠方法模式-Factory Method Pattern
- 工廠三兄弟之工廠方法模式(一)
- 工廠三兄弟之工廠方法模式(二)
- 工廠三兄弟之工廠方法模式(三)
- 工廠三兄弟之工廠方法模式(四)
- 抽象工廠模式-Abstract Factory Pattern
- 工廠三兄弟之抽象工廠模式(一)
- 工廠三兄弟之抽象工廠模式(二)
- 工廠三兄弟之抽象工廠模式(三)
- 工廠三兄弟之抽象工廠模式(四)
- 工廠三兄弟之抽象工廠模式(五)
- 單例模式-Singleton Pattern
- 確保對象的唯一性——單例模式 (一)
- 確保對象的唯一性——單例模式 (二)
- 確保對象的唯一性——單例模式 (三)
- 確保對象的唯一性——單例模式 (四)
- 確保對象的唯一性——單例模式 (五)
- 原型模式-Prototype Pattern
- 對象的克隆——原型模式(一)
- 對象的克隆——原型模式(二)
- 對象的克隆——原型模式(三)
- 對象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 復雜對象的組裝與創建——建造者模式(一)
- 復雜對象的組裝與創建——建造者模式(二)
- 復雜對象的組裝與創建——建造者模式(三)
- 七個結構型模式
- 適配器模式-Adapter Pattern
- 不兼容結構的協調——適配器模式(一)
- 不兼容結構的協調——適配器模式(二)
- 不兼容結構的協調——適配器模式(三)
- 不兼容結構的協調——適配器模式(四)
- 橋接模式-Bridge Pattern
- 處理多維度變化——橋接模式(一)
- 處理多維度變化——橋接模式(二)
- 處理多維度變化——橋接模式(三)
- 處理多維度變化——橋接模式(四)
- 組合模式-Composite Pattern
- 樹形結構的處理——組合模式(一)
- 樹形結構的處理——組合模式(二)
- 樹形結構的處理——組合模式(三)
- 樹形結構的處理——組合模式(四)
- 樹形結構的處理——組合模式(五)
- 裝飾模式-Decorator Pattern
- 擴展系統功能——裝飾模式(一)
- 擴展系統功能——裝飾模式(二)
- 擴展系統功能——裝飾模式(三)
- 擴展系統功能——裝飾模式(四)
- 外觀模式-Facade Pattern
- 深入淺出外觀模式(一)
- 深入淺出外觀模式(二)
- 深入淺出外觀模式(三)
- 享元模式-Flyweight Pattern
- 實現對象的復用——享元模式(一)
- 實現對象的復用——享元模式(二)
- 實現對象的復用——享元模式(三)
- 實現對象的復用——享元模式(四)
- 實現對象的復用——享元模式(五)
- 代理模式-Proxy Pattern
- 設計模式之代理模式(一)
- 設計模式之代理模式(二)
- 設計模式之代理模式(三)
- 設計模式之代理模式(四)
- 十一個行為型模式
- 職責鏈模式-Chain of Responsibility Pattern
- 請求的鏈式處理——職責鏈模式(一)
- 請求的鏈式處理——職責鏈模式(二)
- 請求的鏈式處理——職責鏈模式(三)
- 請求的鏈式處理——職責鏈模式(四)
- 命令模式-Command Pattern
- 請求發送者與接收者解耦——命令模式(一)
- 請求發送者與接收者解耦——命令模式(二)
- 請求發送者與接收者解耦——命令模式(三)
- 請求發送者與接收者解耦——命令模式(四)
- 請求發送者與接收者解耦——命令模式(五)
- 請求發送者與接收者解耦——命令模式(六)
- 解釋器模式-Interpreter Pattern
- 自定義語言的實現——解釋器模式(一)
- 自定義語言的實現——解釋器模式(二)
- 自定義語言的實現——解釋器模式(三)
- 自定義語言的實現——解釋器模式(四)
- 自定義語言的實現——解釋器模式(五)
- 自定義語言的實現——解釋器模式(六)
- 迭代器模式-Iterator Pattern
- 遍歷聚合對象中的元素——迭代器模式(一)
- 遍歷聚合對象中的元素——迭代器模式(二)
- 遍歷聚合對象中的元素——迭代器模式(三)
- 遍歷聚合對象中的元素——迭代器模式(四)
- 遍歷聚合對象中的元素——迭代器模式(五)
- 遍歷聚合對象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 協調多個對象之間的交互——中介者模式(一)
- 協調多個對象之間的交互——中介者模式(二)
- 協調多個對象之間的交互——中介者模式(三)
- 協調多個對象之間的交互——中介者模式(四)
- 協調多個對象之間的交互——中介者模式(五)
- 備忘錄模式-Memento Pattern
- 撤銷功能的實現——備忘錄模式(一)
- 撤銷功能的實現——備忘錄模式(二)
- 撤銷功能的實現——備忘錄模式(三)
- 撤銷功能的實現——備忘錄模式(四)
- 撤銷功能的實現——備忘錄模式(五)
- 觀察者模式-Observer Pattern
- 對象間的聯動——觀察者模式(一)
- 對象間的聯動——觀察者模式(二)
- 對象間的聯動——觀察者模式(三)
- 對象間的聯動——觀察者模式(四)
- 對象間的聯動——觀察者模式(五)
- 對象間的聯動——觀察者模式(六)
- 狀態模式-State Pattern
- 處理對象的多種狀態及其相互轉換——狀態模式(一)
- 處理對象的多種狀態及其相互轉換——狀態模式(二)
- 處理對象的多種狀態及其相互轉換——狀態模式(三)
- 處理對象的多種狀態及其相互轉換——狀態模式(四)
- 處理對象的多種狀態及其相互轉換——狀態模式(五)
- 處理對象的多種狀態及其相互轉換——狀態模式(六)
- 策略模式-Strategy Pattern
- 算法的封裝與切換——策略模式(一)
- 算法的封裝與切換——策略模式(二)
- 算法的封裝與切換——策略模式(三)
- 算法的封裝與切換——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 訪問者模式-Visitor Pattern
- 操作復雜對象結構——訪問者模式(一)
- 操作復雜對象結構——訪問者模式(二)
- 操作復雜對象結構——訪問者模式(三)
- 操作復雜對象結構——訪問者模式(四)
- 設計模式趣味學習(復習)
- 設計模式與足球(一)
- 設計模式與足球(二)
- 設計模式與足球(三)
- 設計模式與足球(四)
- 設計模式綜合應用實例
- 多人聯機射擊游戲
- 多人聯機射擊游戲中的設計模式應用(一)
- 多人聯機射擊游戲中的設計模式應用(二)
- 數據庫同步系統
- 設計模式綜合實例分析之數據庫同步系統(一)
- 設計模式綜合實例分析之數據庫同步系統(二)
- 設計模式綜合實例分析之數據庫同步系統(三)