基本概念 Singleton是一種建立性模型,它用來確保只產生一個執行個體,並提供一個訪問它的全域訪問點.對一些類來說,保證只有一個執行個體是很重要的,比如有的時候,資料庫連接或Socket串連要受到一定的限制,必須保持同一時間只能有一個串連的存在.再舉個例子,集合中的set中不能包含重複的元素,添加到set裡的對象必須是唯一的,如果重複的值添加到set,它只接受一個執行個體.JDK中正式運用了Singleton模式來實現 set的這一特性,大家可以查看java.util.Collections裡的內部靜態類SingletonSet的原代碼.其實Singleton是最簡單但也是應用最廣泛的模式之一,在JDK 中隨處可見. 簡單分析 為了實現 Singleton模式,我們需要的是一個靜態變數,能夠在不建立對象的情況下記憶是否已經產生過執行個體了.靜態變數或靜態方法都可以在不產生具體執行個體的情況下直接調用,這樣的變數或方法不會因為類的執行個體化而有所改變.在圖1的結構中可以看到,uniqueInstance就是這個獨立的靜態變數,它可以記憶對象是否已經執行個體化了,在靜態方法Instance中對這個變數進行判斷,若沒有執行個體化過就產生一個新的對象,如果已經執行個體化了則不再產生新的對象,仍然返回以前產生的執行個體. 圖1: Singleton 模式結構 具體實施 實現 Singleton 模式的辦法通常有三種. 一. 用靜態方法實現 Singleton這種方法是使用靜態方法來監視執行個體的建立.為了防止建立一個以上的執行個體,我們最好把構造器聲明為private. 這樣可以防止客戶程式員通過除由我們提供的方法之外的任意方式來建立一個執行個體,如果不把構造器聲明為private,編譯器就會自作聰明的自動同步一個預設的friendly構造器.這種實現方法是最常見的,也就是圖1中結構的標準實現.
public class Singleton { private static Singleton s; private Singleton(){}; /** * Class method to access the singleton instance of the class. */ public static Singleton getInstance() { if (s == null) s = new Singleton(); return s; } } // 測試類別 class singletonTest { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); if (s1==s2) System.out.println("s1 is the same instance with s2"); else System.out.println("s1 is not the same instance with s2"); } } |
singletonTest運行結果是: s1 is the same instance with s2 這證明我們只建立了一個執行個體. 二. 以靜態變數為標誌實現 Singleton在類中嵌入一個靜態變數做為標誌,每次都在進入構造器的時候進行檢查. 問題在於構造器沒有傳回型別,如果確定建立一個執行個體成功與否.一個方法是調用一個函數來檢查建立是否成功,然後簡單的返回一個來自靜態變數的值,但是這樣做是不優雅的,而且容易發生錯誤.比較好的做法是建立一個當建立了一個以上的執行個體時可以拋出異常的類,這個類僅僅是調用父類方法,好處是用了自己命名的異常類型,錯誤資訊更加清晰:
class SingletonException extends RuntimeException { public SingletonException(String s) { super(s); } } class Singleton { static boolean instance_flag = false; // true if 1 instance public Singleton() { if (instance_flag) throw new SingletonException("Only one instance allowed"); else instance_flag = true; // set flag for 1 instance } } // 測試類別 public class singletonTest { static public void main(String argv[]) { Singleton s1, s2; // create one incetance--this should always work System.out.println("Creating one instance"); try { s1 = new Singleton(); } catch (SingletonException e) { System.out.println(e.getMessage()); } // try to create another spooler --should fail System.out.println("Creating two instance"); try { s2 = new Singleton(); } catch (SingletonException e) { System.out.println(e.getMessage()); } } } |
singletonTest運行結果是: Creating one instance Creating two instance Only one instance allowed 可以看出,第一個執行個體順利建立,第二個執行個體建立實拋出了我們自訂的異常. 三. 用註冊器機制來建立 Singleton首先用集合中的Hashtable 和Enumeration來實現addItem(Object key,Object value),getItem(Object key), ,removeItem(Objectkey)等方法實現一個管理器,將key和value一一關聯起來,客戶程式員建立執行個體前首先用addItem方法進行註冊,再用getItem方法擷取執行個體.Hashtable中的key是唯一的,從而保證建立的執行個體是唯一的,具體實現限於篇幅不再細說,在Prototype模型的應用一文中我將會給出一個實現註冊器的代碼.用註冊器機制來建立Singleton模式的好處是易於管理,可以同時控制多個不同類型的Singleton執行個體. 小結
- Singleton模式可以方便的進行擴充,產生指定數目的執行個體.
- 在The Design Patterns Java Companion一書中曾提到過用靜態類的方式來實現Singleton模式,並指出java.lang.Math就是一個例子,這裡我並不表示贊同,因為Math並不是一個真正的對象,我們只是直接調用Math類所封裝的靜態方法而已,根本就沒有建立執行個體的過程,又從何說起只產生一個執行個體呢?這個問題我曾到Javaranch的論壇上發過文章,所有回帖的人也都是對這一觀點持否定態度.
- 在多線程的程式中,singleton可能會變的不可靠,可能會出現多個執行個體,解決的辦法很簡單,加個同步修飾符:public static synchronized Singleton getInstance().這樣就保證了線程的安全性.
- 最後要說的是大家可能會看見一些其他實現Singleton模式的方法,因為模式在具體的應用時是靈活的,不是一成不變的,並沒有一個固定的做法,但大都是上面幾種方法的變形.
|