簡體   English   中英

如何在Java中實現抽象的單例類?

[英]How can I implement an abstract singleton class in Java?

這是我的示例抽象單例類:

public abstract class A {
    protected static A instance;
    public static A getInstance() {
        return instance;
    }
    //...rest of my abstract methods...
}

以下是具體實施:

public class B extends A {
    private B() { }
    static {
        instance = new B();
    }
    //...implementations of my abstract methods...
}

不幸的是我無法在B類中獲取靜態代碼來執行,因此實例變量永遠不會被設置。 我試過這個:

Class c = B.class;
A.getInstance() - returns null;

還有這個

ClassLoader.getSystemClassLoader().loadClass("B");
A.getInstance() - return null;

在eclipse調試器中運行這兩個,靜態代碼永遠不會被執行。 我可以找到執行靜態代碼的唯一方法是將B的構造函數的可訪問性更改為public,並調用它。

我在Ubuntu 32bit上使用sun-java6-jre來運行這些測試。

摘要單身人士? 對我來說聽起來不太可行。 Singleton模式需要private構造函數,這已經使子類化成為不可能。 你需要重新考慮你的設計。 抽象工廠模式可能更適合於特定目的。

你試圖讓一個抽象類扮演兩個截然不同的角色:

  • 可以具有多個可替換實現的(單例)服務的抽象工廠角色,
  • 服務接口角色,

最重要的是,你還希望服務是單例並在整個類系列上強制執行'singletoness',因為某些原因你不考慮緩存服務實例。

有人(我會)說它聞起來非常糟糕,因為多種原因它違反了關注點,單身人士無法進行單元測試“等等。

其他人會說它沒關系,它不需要很多不同的基礎設施,並且在一些非常常見的第​​三方(傳統)Java API中有一些流暢的界面。

不好的部分要求孩子們選擇父工廠方法返回的實現。 這個責任應該被推高並集中到抽象的超類中。 否則,您將在非常不同的上下文中使用的模式混合在一起,抽象工廠(父級決定客戶將要獲得的類家族)和工廠方法(子工廠選擇客戶端將獲得的內容)。

Factory Method實際上也不可能,因為您不能覆蓋靜態方法或構造函數。

有一些(丑陋的)方法來實現你的目標:

public abstract class A{
    public static A getInstance(...){
      if (...)
         return B.getInstance();
      return C.getInstance();
    }

    public abstract void doSomething();

    public abstract void doSomethingElse();

}

public class B extends A{
    private static B instance=new B();

    private B(){
    }

    public static B getInstance(){
        return instance;
    }

    public void doSomething(){
        ...
    }
    ...
}

//do similarly for class C

父級也可以使用反射,緩存實例等。

更加友好的測試和擴展解決方案只是標准的關注點分離。 這些孩子本身不再是單身,但你將它們打包成一些內部包,你將其記錄為“私有”,外部包中的公共抽象父將處理子實例的緩存或池化,強制執行任何實例化這些課程需要政策。

A.getInstance()永遠不會調用派生實例,因為它是靜態綁定的。

我將對象的創建與實際對象本身分開,並創建一個返回特定類類型的適當工廠 目前尚不清楚你如何參數化,給定你的示例代碼 - 是通過一些參數進行參數化,還是類選擇是靜態的?

你可能想重新考慮單身,順便說一句。 這是一個常見的反模式 ,並使測試(特別是)成為一種痛苦,因為測試中的類將提供他們自己的該類實例作為單例。 您不能提供虛擬實現,也不能(輕松地)為每個測試創建新實例。

單身人士有點討厭 摘要堅持繼承,如果可能的話,你經常要避免這種繼承。 總的來說,我會重新思考,如果你想要做的是最簡單的方法 ,如果是這樣,那么一定要使用工廠而不是單身人士(單身人士在單元測試中很難替代,而工廠可以被告知替代輕松測試實例)。

一旦你開始考慮將它作為一個工廠實現,抽象的東西就會自行解決(要么它顯然是必要的,要么可以很容易地代替接口)。

除了其他人指出的問題之外,在A中使用instance字段意味着在整個VM中只能有一個單例。 如果你還有:

public class C extends A {
    private C() { }
    static {
        instance = new C();
    }
    //...implementations of my abstract methods...
}

...然后最后加載的BC任何一個都將獲勝,而另一個的單例實例將丟失。

這只是做事的壞方法。

我發現了在抽象類中使用Singleton的更好方法,它使用靜態Map來維護子類的實例。

public abstract class AbstractSingleton {

    private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();

    AbstractSingleton() throws SingletonException {
        String clazzName = this.getClass().getName();
        if (registryMap.containsKey(clazzName)) {
            throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
        } else {
            synchronized (registryMap) {
                if (registryMap.containsKey(clazzName)) {
                    throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
                } else {
                    registryMap.put(clazzName, this);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    T instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    public static AbstractSingleton getInstance(final String clazzName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (!registryMap.containsKey(clazzName)) {
            Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    AbstractSingleton instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return registryMap.get(clazzName);
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                    T instance = constructor.newInstance(initargs);
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    static class SingletonException extends Exception {
        private static final long serialVersionUID = -8633183690442262445L;

        private SingletonException(String message) {
            super(message);
        }
    }
}

來自: https//www.cnblogs.com/wang9192/p/3975748.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM