2010-03-17 2 views
18

Вот мой пример абстрактного синглтон класс:Как я могу реализовать абстрактный одноэлементный класс в 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; 

Запуск оба эти в затмении отладчик статический код никогда не будет выполнен. Единственный способ найти исполняемый статический код - это изменить доступность конструктора B к публичному и вызвать его.

Я использую sun-java6-jre на Ubuntu 32bit для запуска этих тестов.

ответ

19

Abstract Singleton? Не кажется мне жизнеспособным. Для шаблона Singleton требуется конструктор private, и это уже делает невозможным подклассирование. Вам нужно будет пересмотреть свой дизайн. Abstract Factory pattern может быть более подходящим для конкретной цели.

+0

Цель частного конструктора в Синглтоне состоит в том, чтобы помешать кому-либо еще его создавать. Я достиг этого здесь - вы не можете создать экземпляр абстрактного класса, а подкласс имеет частный конструктор. – Simon

+0

Это не приводит к тому, что подкласс является абстрактным и имеет только частный конструктор. – BalusC

+0

Singleton не создает _require_ частный конструктор (см. Мой ответ для решения этого вопроса с помощью открытого конструктора). – ryvantage

4

A.getInstance() никогда не будет вызывать производный экземпляр, поскольку он статически связан.

Я бы отделил создание объекта от самого фактического объекта и создал подходящий factory, возвращающий определенный тип класса. Неясно, как вы могли бы параметризовать это, учитывая ваш пример кода - он параметризуется с помощью некоторого аргумента или является статическим выбором класса?

Возможно, вы захотите переосмыслить синглтон, кстати. Это распространенный antipattern и делает тестирование (в частности) болью, поскольку тестируемые классы предоставят собственный экземпляр этого класса в качестве одноэлементного. Вы не можете предоставить фиктивную реализацию и (легко) создать новый экземпляр для каждого теста.

+0

A.getInstance() не нужно вызывать производный экземпляр, так как он возвращает переменную экземпляра. Выведенный экземпляр (класс B) задает переменную экземпляра. За исключением того, что статический кодовый блок не запускается. Если я не могу это исправить, тогда может быть мой единственный вариант. – Simon

4

Синглтоны - это вид yucky. Резюме настаивает на наследовании, которое вы чаще всего не хотите avoid, если это возможно. В целом я бы передумал, если то, что вы пытаетесь сделать, это simplest possible way, и если да, то обязательно используйте фабрику, а не синглтон (синглтоны, как правило, трудно заменить в unit tests, тогда как фабрикам может быть предложено легко заменить тестовые экземпляры).

После того как вы начнете изучать его как фабрику, абстрактная вещь будет сортироваться (либо она будет явно необходима, либо она может легко исключаться вместо интерфейса).

+0

+1 для указания, что синглтоны являются анти-образными. –

+1

Синглотоны имеют свое место, но, как правило, лучший дизайн дизайна для всего, что вы делаете. Журнал, вероятно, лучший пример, который я могу придумать для синглтона - таким образом вам не нужно передавать переменную и иметь доступ к ней из любого места. –

+0

Журнал может быть сделан намного лучше с помощью инъекции. Одиночный журнал не может легко рассказать вам, из какого класса вышла строка, тогда как Log l = new Log (this.getClass()) или какой-либо его varient. С инъекцией это просто журнал @inject или что-то подобное. Синглтон также сложнее тестировать, даже для ведения журнала. Снова синглтон - это самый худший выбор (но, по общему признанию, годный к употреблению) –

2

В дополнение к проблемам других указали, имея instance поле в A означает, что вы можете иметь только один одноплодной во всей виртуальной машины. Если у вас также есть:

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

... тогда какой из B или C загружается последний победит, и синглтон экземпляр другой будет потерян.

Это просто плохой способ сделать что-то.

+0

Спасибо - и другие, которые сказали то же самое. Это подчеркивает, что моя реальная проблема заключается в том, как я выбираю одну из нескольких реализаций моего интерфейса, которую я должен раскрывать. Фабрика или Фасад выглядят как хорошие способы. – Simon

8

Ну, из вашего поста, даже если это не ясно сказано, звучит так, будто вы хотите, чтобы абстрактный класс играл две разные роли. Ролей являются:

  • абстрактного завод роли для (Singleton) служб , который может иметь несколько взаимозаменяемых реализаций,
  • интерфейс службы роли ,

плюс вы хотите сервис, который будет использоваться в одиночном режиме, «singletoness» во всем семействе классов, по какой-то причине вам недостаточно кэшировать экземпляр службы.

Это прекрасно.

Кто-то скажет, что он плохо пахнет, потому что «нарушает разделение проблем», а «синглтоны и модульные тесты не идут хорошо вместе».

Кто-то еще скажет, что это нормально, потому что вы даете ответственность за создание прав детей в самой семье, статический метод.

Неправильно то, что после того, как вы захотите, чтобы дети несли ответственность за выбор того, какую реализацию должен вернуть родительский заводский метод. Это неправильно с точки зрения дизайна, потому что вы делегируете всем детям то, что можно просто подтолкнуть и централизовать в абстрактный суперкласс, а также продемонстрировать, что вы смешиваете шаблоны, которые используются в разных контекстах, Abstract Factory (родительский решает, какое семейство клиенты классов будут получать) и 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 

Родитель мог также использовать отражение.

Другим решением, дружественным к среде и расширением, является просто наличие детей, которые не являются одиночными, но упакованы в какой-либо внутренний пакет, который вы задокументируете как «частный» и абстрактный родитель, который может выставить статический getInstance() и «singleton mimiking» будет кэшировать дочерние экземпляры, обеспечивающие, чтобы клиенты всегда получали один и тот же экземпляр службы.

Смежные вопросы