[英]How to implement a subclassable Singleton in Java
我正在尋找一種方法來實現一個抽象類(或有效抽象),該類只強制每個子類的一個實例。
我相當確定用工廠實現這將非常簡單,但我很想知道它是否可以在不知道所有子類類型的情況下完成,即通用的單例執行器類。
現在我主要只是在玩弄這樣的想法,所以我不是在這里尋找對設計選擇提出質疑的反饋。
我工作的語言是Java,但現在我不一定擔心實現細節,除非在Java中不可能,然后,當然,提供證據證明它是不可能的。
我想知道你想做什么。 腦海中浮現出幾種可能性,知道這將走向何方可能會有所幫助。
選項1
所以你可以嘗試使用enum
類型作為你的抽象基類。 然后,語言保證每個枚舉常量都是單例。 枚舉可以有常量實現的抽象方法。 這將起作用,但是如果您有很多實現常量和很多抽象方法要實現,編譯單元會變得非常大且難以導航。 如果開始失控,您當然可以將一些工作委派給助手類。
選項 2
您可以做的是讓基類構造函數檢查它的實際類型並將其存儲在靜態 HashSet (或類似)中。 如果一個條目已經存在,那么你有同一個單例的兩個實例。 就像是
public abstract class BaseClass {
private static HashSet<Class<?>> instances = new HashSet<>();
protected BaseClass() {
checkInstances();
}
private synchronized void checkInstances() {
boolean duplicate = instances.add(getClass());
if (duplicate) {
throw new RuntimeException("Duplicate class " + getClass().getName());
}
}
}
這樣做的缺點是錯誤發生在運行時並且代碼不是特別漂亮,正如您所看到的,您可能需要考慮集合的同步
選項 3
您的最后一個選擇就是不要求基類強制執行此限制。 派生類的工作可能應該決定它們是否是單例。 派生類中的私有構造函數是最簡單的方法。
結論
我個人會實施選項 1 或選項 3,因為您不會遇到運行時故障。
首先,通用單例沒有意義。
父類不應該負責檢索和管理其子類的實例。
它以兩種方式(父-> 子和子-> 父)創建強耦合。
其次,正如 shmosel 所說,不可能對單例(沒有特殊工件)進行子類化。
單例模式的關鍵是缺乏在單例類之外實例化類的能力,因此不必提供公共構造函數。
在這些情況下,如何對單例類進行子類化?
要允許對單例類進行子類化,您必須有一個公共構造函數,同時確保該類的實例不超過一個。
反轉控制容器如 Spring 可能會這樣做(這是一個特殊工件的例子)。
作為旁注,我不考慮對訪問修飾符進行調整,例如可以允許對單例進行子類化的package-private
修飾符,但它的限制是單例只能是包外的單例。
我想說,單身人士不好。 但是發現這個問題很有趣,所以我創建了你想要的。 這是代碼
public static abstract class SingletonBase {
private static HashSet<SingletonBase> instances = new HashSet<>();
{
for (SingletonBase sb : instances) {
if (sb.getClass() == this.getClass()) throw new RuntimeException("there is already 1 instance");
}
}
public static <E> E getInstance(Class<E> clazz) {
if (!SingletonBase.class.isAssignableFrom(clazz)) {
throw new RuntimeException();
}
for (SingletonBase sb : instances) {
if (sb.getClass() == clazz) return (E) sb;
}
try {
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
private SingletonBase() {
instances.add(this);
}
}
static class SingletonTest extends SingletonBase{
}
static class SecondSingletonTest extends SingletonBase{
}
public static void main(String[] args) {
for(int i=0;i<=10;i++)
System.out.println( SingletonBase.getInstance(SingletonTest.class));
for(int i=0;i<=10;i++)
System.out.println( SingletonBase.getInstance(SecondSingletonTest.class));
//throws exception, because we try to create second instance here
new SingletonTest();
}
創建泛型類的方法存在一些問題,這里解決了:首先,您不能創建多個實例,因此基類必須跟蹤所有實例,當您嘗試使用 new 創建另一個實例時,它會拋出例外。 其次,您需要獲取特定類的實例。 如果您不想創建這樣的實例:
SingletonBase.getInstance(SecondSingletonTest.class)
您可以像這樣創建子類:
static class SingletonTest extends SingletonBase{
public static SingletonTest getInstance(){
return getInstance(SingletonTest.class);
}
}
也有人建議使用 ENUM 方法,它很容易實現,但打破了 SOLID 的開閉原則
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.