複製鏈接
請複製以下鏈接發送給好友

單例模式

鎖定
單例模式,屬於創建類型的一種常用的軟件設計模式。通過單例模式的方法創建的類在當前進程中只有一個實例(根據需要,也有可能一個線程中屬於單例,如:僅線程上下文內使用同一個實例)
中文名
單例模式
外文名
Singleton pattern
類    別
設計模式
解    釋
常用的軟件設計模式

單例模式定義

數學與邏輯學中,singleton定義為“有且僅有一個元素的集合”。
單例模式最初的定義出現於《設計模式》(艾迪生維斯理, 1994):“保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。”
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;
    }
}


單例模式簡介

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

單例模式實現方式

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

單例模式動機

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

單例模式要點

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

單例模式優缺點

單例模式優點

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

單例模式缺點

一、開銷
雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
二、可能的開發混淆
使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基於.NET Framework的語言),只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如 C++),其他類可以刪除對象實例,但這樣會導致單例類中出現懸浮引用。 [2] 
參考資料
  • 1.    (美)鍾冠賢.Objective-C編程之道 iOS設計模式解析.北京市:人民郵電出版社,2011:80
  • 2.    劉偉主編.設計模式.北京市:清華大學出版社,2011:134-135