Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

Ответ 2

Вы пытаетесь заставить абстрактный класс играть две совершенно разные роли:

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

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

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

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

Плохая часть требует, чтобы дети выбирали, какую реализацию должен возвращать родительский фабричный метод. Эта ответственность должна быть повышена и централизована в абстрактный суперкласс. В противном случае вы смешиваете шаблоны, которые используются в самых разных контекстах: Абстрактная фабрика (родитель решает, какое семейство классов получат клиенты) и Фабричный метод (дочерние фабрики выбирают, что получат клиенты).

Фабричный метод также практически невозможен, потому что вы не можете переопределить ни статические методы, ни конструкторы.

Хотя есть несколько (безобразных) способов достижения вашей цели:

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

Родитель также может использовать отражение, экземпляры кэша и т.д.

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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