|
考慮使用 Singleton 模式 時擁有子類別的問題,在Singleton模式中的getInstance()通常是一個靜態方法,不能在子類別中重新定義它,關於子類別實例的產生交由getInstance()來進行是最好的選擇,例如: public class Singleton {
private static Singleton instance = null; private Singleton() { // .... } public static Singleton getInstance() { if (instance == null) { // getEnv表示系統環境變數 String style = getEnv("style"); if (style.equals("child1")) instance = new ChildSingleton1(); else if (style.equals("child2r")) instance = new ChildSingleton2(); else instance = new Singleton(); } return _instance; } // .... } 上面這個程式片段改寫自 Gof 書中關於Singleton的例子,並用Java實現;在書中指出,這個例子的缺點是每增加一個子類別,getInstance()就必須重新修改,這個問題在Java中可以使用Reflection機制來解決: public class Singleton {
private static Singleton instance = null; private Singleton() { // .... } public static Singleton getInstance() { if (instance == null) { // getEnv表示環境變數 String style = getEnv("style"); try { instance = (Singleton) Class.forName(style).newInstance(); } catch(Exception e) { System.out.println( "Sorry! No such class defined!"); } } return instance; } // .... } 上面的方式使用了Java的Reflection機制,並透過環境變數設定要產生的子類Singleton,如果不使用Reflection的話,Gof 書中提出的改進方法是使用Registry of Singleton方法: import java.util.*;
public class Singleton { // 註冊表,用於註冊子類別物件 private static Map registry = new HashMap(); private static Singleton instance; public static void register( String name, Singleton singleton) { registry.put(name, singleton); } public static Singleton getInstance() { if (instance == null) { // getEnv表示取得環境變數 String style = getEnv("style"); instance = lookup(style); } return instance; } protected static Singleton lookup(String name) { return (Singleton) registry.get(name); } } 在Gof書中使用List來實現註冊表,而在這邊使用HasMap類別來實現,它是由Java SE所提供的;在父類別中提供一個register() 以註冊Singleton的子類別所產生之實例,而註冊的時機可以放在子類別的建構方法中加以實現,例如: public class ChildSingleton1 extends Singleton {
public ChildSingleton1() { // .... // 註冊子類別物件 register(getClass().getName(), this); } } 若要利用Singleton,則先使用這個子類別產生物件,這會向父類別註冊物件,之後透過Singleton父類別來取得物件: // 必須先啟始這段註冊程序
// 產生並註冊ChildSingleton1物件 new ChildSingleton1(); // 產生並註冊ChildSingleton2物件 new ChildSingleton2(); // 註冊完成,可以使用父類別來取得子類的Singleton
// 至於取回何哪一個,視您的環境變數設置決定 Singleton childSingleton = Singleton.getInstance(); 這種方式的缺點是您必須在程式中啟始一段程序,先向父類別註冊子類的Singleton,之後才能透過父類別來取得指定的子類別Singleton實例, 好處是可以適用於沒有Reflection機制的語言,如果您想要改變Singleton父類傳回的子類Singleton,可以在上面的 Singleton類別中加入一個reset()方法,將instance設定為null,然後重新設定環境變數,之後再利用 Singleton父類的getInstance()方法重新取得註冊表中的其它子類。 事實上Registry of Singleton的真正優點正在於此,您可以使用父類別來統一管理多個繼承的子類別之Singleton實例,您可以在需要的時候再向父類別註冊子類 Singleton,必要時隨時調整傳回的子類別Singleton。 |