單例模式

軟體設計模式之一

單例模式,是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個實例。即一個類只有一個對象實例。

定義


邏輯,義“且僅元素集合”。
單例模式初義《設計模式》(艾迪維, ):“保證類僅例,提供訪局訪。”
Java單例模式義:“類且僅例,且例化整系統提供。”
Java單例模式例子
public class Singleton { private Singleton(){ } private static volatile Singleton instance = null; public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}

簡介


單例模式
單例模式
單例模式是設計模式中最簡單的形式之一。這一模式的目的是使得類的一個對象成為系統中的唯一實例。要實現這一點,可以從客戶端對其進行實例化開始。因此需要用一種只允許生成對象類的唯一實例的機制,“阻止”所有想要生成對象的訪問。使用工廠方法來限制實例化過程。這個方法應該是靜態方法(類方法),因為讓類的實例去生成另一個唯一實例毫無意義。

構建方式


通常單例模式在Java語言中,有兩種構建方式:
懶漢式—線程不安全:最基礎的實現方式,線程上下文單例,不需要共享給所有線程,也不需要加synchronize之類的鎖,以提高性能。
懶漢式—線程安全:加上synchronize之類保證線程安全的基礎上的懶漢模式,相對性能很低,大部分時間並不需要同步
餓漢方式。指全局的單例實例在類裝載時構建。
雙檢鎖式。在懶漢式基礎上利用synchronize關鍵字和volatile關鍵字確保第一次創建時沒有線程間競爭而產生多個實例,僅第一次創建時同步,性能相對較高
登記式。作為創建類的全局屬性存在,創建類被裝載時創建
枚舉。java中枚舉類本身也是一種單例模式

動機


對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個列印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內容完全一致,則是重複對象,浪費內存資源;如果這些窗口顯示的內容不一致,則意味著在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變數可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。

要點


顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
從具體實現角度來說,就是以下三點:一是單例模式的類只提供私有的構造函數,二是類定義中含有一個該類的靜態私有對象,三是該類提供了一個靜態的公有的函數用於創建或獲取它本身的靜態私有對象。
在下面的對象圖中,有一個"單例對象",而"客戶甲"、"客戶乙" 和"客戶丙"是單例對象的三個客戶對象。可以看到,所有的客戶對象共享一個單例對象。而且從單例對象到自身的連接線可以看出,單例對象持有對自己的引用。
一些資源管理器常常設計成單例模式。
在計算機系統中,需要管理的資源包括軟體外部資源,譬如每台計算機可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個列印作業同時輸出到印表機中。每台計算機可以有若干傳真卡,但是只應該有一個軟體負責管理傳真卡,以避免出現兩份傳真作業同時傳到傳真卡中的情況。每台計算機可以有若干通信埠,系統應當集中管理這些通信埠,以避免一個通信埠同時被兩個請求同時調用。
需要管理的資源包括軟體內部資源,譬如,大多數的軟體都有一個(甚至多個)屬性(properties)文件存放系統配置。這樣的系統應當由一個對象來管理一個屬性文件。
需要管理的軟體內部資源也包括譬如負責記錄網站來訪人數的部件,記錄軟體系統內部事件、出錯信息的部件,或是對系統的表現進行檢查的部件等。這些部件都必須集中管理,不可整出多頭。
這些資源管理器構件必須只有一個實例,這是其一;它們必須自行初始化,這是其二;允許整個系統訪問自己這是其三。因此,它們都滿足單例模式的條件,是單例模式的應用。

優缺點


優點

一、實例控制
單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
二、靈活性
因為類控制了實例化過程,所以類可以靈活更改實例化過程。

缺點

一、開銷
雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
二、可能的開發混淆
使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基於.NET Framework的語言),只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如 C++),其他類可以刪除對象實例,但這樣會導致單例類中出現懸浮引用。