單例模式
單例模式顧名思義,就是只有一個實例。
作為對象的創建模式[GOF95], 單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。這個類稱為單例類。
簡介
單例模式是設計模式中最簡單的形式之一。這一模式的目的是使得類的一個對象成為系統中的唯一實例。要實現這一點,可以從客戶端對其進行實例化開始。因此需要用一種只允許生成對象類的唯一實例的機制,“阻止”所有想要生成對象的訪問。使用工廠方法來限制實例化過程。這個方法應該是靜態方法(類方法),因為讓類的實例去生成另一個唯一實例毫無意義。
StaticuniqueInstance是singleton的唯一實例,staticsharedInstance將把它返回客戶端。通常,sharedInstance會檢查uniqueInstance是否已經被實例化。如果沒有,它會生成一個實例然後返回uniqueInstance。
動機
對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個列印任務,但是只能有一個正在工作的任務;一個系統只能有一個視窗管理器或檔案系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對視窗對象進行唯一化,將彈出多個視窗,如果這些視窗顯示的內容完全一致,則是重複對象,浪費記憶體資源;如果這些視窗顯示的內容不一致,則意味著在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變數可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。
單例模式的要點
顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。在下面的對象圖中,有一個"單例對象",而"客戶甲"、"客戶乙" 和"客戶丙"是單例對象的三個客戶對象。可以看到,所有的客戶對象共享一個單例對象。而且從單例對象到自身的連線線可以看出,單例對象持有對自己的引用。一些資源管理器常常設計成單例模式。
在計算機系統中,需要管理的資源包括軟體外部資源,譬如每台計算機可以有若干個印表機,但只能有一個Printer spooler, 以避免兩個列印作業同時輸出到印表機中。每台計算機可以有若干傳真卡,但是只應該有一個軟體負責管理傳真卡,以避免出現兩份傳真作業同時傳到傳真卡中的情況。每台計算機可以有若干通信連線埠,系統應當集中管理這些通信連線埠,以避免一個通信連線埠同時被兩個請求同時調用。
需要管理的資源包括軟體內部資源,譬如,大多數的軟體都有一個(甚至多個)屬性(properties)檔案存放系統配置。這樣的系統應當由一個對象來管理一個屬性檔案。
需要管理的軟體內部資源也包括譬如負責記錄網站來訪人數的部件,記錄軟體系統內部事件、出錯信息的部件,或是對系統的表現進行檢查的部件等。這些部件都必須集中管理,不可政出多頭。
實例
這些資源管理器構件必須只有一個實例,這是其一;它們必須自行初始化,這是其二;允許整個系統訪問自己這是其三。因此,它們都滿足單例模式的條件,是單例模式的套用。在java中,可以使用以下這種方式使用單例模式創建類的實例:
public class MyBean {
private static MyBean instance = null;
private MyBean(){
//do something
}
public static synchronized MyBean getInstance(){
if(instance == null){
instance = new MyBean();
}
return instance;
}
}
當一個類的實例可以有且只可以一個的時候就需要用到了。為什麼只需要有一個呢?有人說是為了節約記憶體。本人對這個說法持保留態度。只有一個實例確實減少記憶體占用,可是我認為這不是使用單例模式的理由。我認為使用單例模式的時機是當實例存在多個會引起程式邏輯錯誤的時候。比如類似有序的號碼生成器這樣的東西,怎么可以允許一個套用上存在多個呢?
singleton模式主要作用是保證在Java應用程式中,一個類Class只有一個實例存在。
一般Singleton模式通常有兩種形式:
第一種形式: 也是常用的形式。
public class Singleton {
private static Singleton instance = null;
private Singleton(){
//do something
}
//這個方法比下面的有所改進,不用每次都進行生成對象,只是第一次使用時生成實例,提高了效率
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
第二種形式:
public class Singleton {
//在自己內部定義自己的一個實例,只供內部調用
private static Singleton instance = new Singleton();
private Singleton(){
//do something
}
//這裡提供了一個供外部訪問本class的靜態方法,可以直接訪問
public static Singleton getInstance(){
return instance;
}
}
Flex中的單例模式
Flex中單例模式,常見的model層實例:
package models
{
import flash.events.EventDispatcher;
import mx.collections.ArrayCollection;
import vo.articlesVO;
import vo.linksVO;
[Bindable]
public class ModelLocator extends EventDispatcher
{
public static var _instance:ModelLocator;
public static function getInstance():ModelLocator{
if(_instance == null){
_instance = new ModelLocator();
}
return _instance;
}
public var total:int;
public var isLogin:Boolean = false;
public var articles:ArrayCollection;
public var selectedArticle:articlesVO;
public var categories:ArrayCollection;
public var links:ArrayCollection;
public var selectedLink:linksVO;
}
}
類中自己完成了自身的實例。。
import models.ModelLocator;
internal function initApp():void{
var instance:ModelLocator = ModelLocator.getInstance();
trace(instance.isLogin);//獲得isLogin
}
]]>
優缺點
優點
一、實例控制
單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
二、靈活性
因為類控制了實例化過程,所以類可以靈活更改實例化過程。
缺點
一、開銷
雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
二、可能的開發混淆
使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫原始碼,因此應用程式開發人員可能會意外發現自己無法直接實例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供記憶體管理的語言中(例如基於.NETFramework的語言),只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如C++),其他類可以刪除對象實例,但這樣會導致單例類中出現懸浮引用。