Вопрос

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

Я могу использовать фабричный метод:

    // A map of existing nodes, for getInstance.
private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static MyClass getInstance(String name) {
    MyClass node = directory.get(name);
    if(node == null) {
       node == new MyClass(name);
    }
    return node;
}

Или в равной степени у меня может быть отдельный метод MyClassFactory.

Но я намеревался создать подкласс MyClass:

public class MySubClass extends MyClass;

Если я больше не буду и вызову MySubClass.getInstance ():

MyClass subclassObj = MySubClass.getInstance("new name");

... тогда subclassObj будет простым MyClass, а не MySubClass.

Однако переопределение getInstance () в каждом подклассе кажется хакерским.

Есть ли у меня аккуратное решение?

<Ч>

Это обобщенная версия вопроса. Больше подробностей, так как ответчики попросили их.

Программа предназначена для генерации ориентированного графа зависимостей между узлами, представляющими части программного обеспечения. Подклассы включают в себя Java-программы, веб-службы, хранимые процедуры SQL, управляемые сообщениями триггеры и т. Д.

Таким образом, каждый класс " is-a " элемент в этой сети, и имеет методы для навигации и изменения отношений зависимости с другими узлами. Разница между подклассами заключается в реализации метода populate(), используемого для настройки объекта из соответствующего источника.

Допустим, узел с именем 'login.java' узнает, что он зависит от 'checkpasswd.sqlpl':

this.addDependency( NodeFactory.getInstance("checkpasswd.sqlpl"));

Проблема в том, что объект checkpasswd.sqlpl может существовать или не существовать в настоящее время.

Это было полезно?

Решение

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

Таким образом, вам нужно будет либо переопределить статический метод в ваших дочерних классах, как вы предлагаете, либо сделать их не статичными, чтобы вы могли наследовать (в иерархии фабричных объектов , а не классов), или передать параметр, чтобы указать тип, который вы хотите создать.

Проверьте метод EnumSet.noneOf (). У него такая же проблема, как и у вас, и он решает ее, передавая метод java.lang.Class. Вы можете использовать newInstance в классе. Но лично я бы просто использовал фабричные объекты, а не классы со статическими методами.

Другие советы

Вы смотрели в Guice? Не уверен, что это точно решит вашу проблему, но он действует как универсальный контейнер для фабрики и зависимости и исключает нетипичные безопасные строковые ключи.

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

используйте интерфейс, такой как:

Public Interface Node<T> {
  public Object<T>    getObject();
  public String       getKey();
  public List<String> getDependencies();
  public void         addDependence(String dep);
}

и затем использовать фабрику для создания экземпляров ваших узлов

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

Таким образом, вам также не нужно знать, какой класс действительно был возвращен с фабрики.

Я бы также сделал MyClass интерфейсом, если вы рассматриваете шаблон Factory.

Класс Class параметризован с типом экземпляра, который он может создать. (например: Class < String > это фабрика для экземпляров String.)

Я не вижу способа обойти знание типа экземпляра, который должен быть создан, когда вы получаете getOrCreate, используя здесь метод фабрики, поэтому я бы рекомендовал передать его методу и параметризовать генерируемый тип:

private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static <T extends MyClass> T getInstance(String name, Class<T> generatorClass)
{
  MyClass node = directory.get(name);    
  if(node == null) {
    node = generatorClass.getConstructor(String.class).newInstance(name);
    directory.put(name, node);
  }
  return node;
}

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

public static MyClass getInstance(String name) {
  return getInstance(name, MyClass.class);
}

Возможно, вы хотите внедрить зависимости. Это обобщило бы то, что вы пытаетесь сделать.

Кроме того, Наследование, вероятно, не совсем то, что вам нужно. Никогда не используйте его, если не можете пройти & Quot; is-a & Quot; тестовое задание. Если ваш унаследованный класс & Quot; Has-a & Quot; уникальный строковый тег (по сути, это то, чем кажется ваш класс), это не означает, что он " is-a " ...

Хотя вы могли бы держать одного за другого. Вероятно, существует довольно много решений, но А) вы не опубликовали весь список требований, поэтому мы можем только догадываться, и Б) что вам, вероятно, нужно, это внедрение зависимостей. :)

Шаблон выглядит как своего рода Flyweight (структурно , если не идеально подходит для намерения.)

Метод populate, как описано, может быть сопоставлен с шаблоном Template, хотя он не обязательно учитывает выраженные проблемы.

Я хотел бы предложить обобщение фабрики с методом create (вместо getInstance, что в любом случае подразумевает для меня Singleton) для различных типов, которые вы ожидаете.

public static MyClass createJavaNode(String name, <differentiator>);
public static MyClass createSqlPlNode (String name, <differentiator>);
.
.
.

Знание того, как name отображается на <differentiator>, действительно является выбором реализации. В идеале, это будет полиморфизм node, а дифференцирование будет по типу MyClass. Метод abstract возвращает TANSTAAFL, но на самом деле возвращает подклассы. Я бы настоятельно рекомендовал сделать Node интерфейсным или абстрактным классом с помощью метода switch Decorator (есть <=>).

Из описания на самом деле видно, что поведение типов отличается между типами, а не поведение самих подклассов, поэтому случай рефакторинга <=> в интерфейс или абстрактный класс получает сильнее.

Перефразируя Р. А. Хайнлайна, <=> - Там нет такого понятия, как бесплатный обед - где-то знание того, как создавать различные типы, должно существовать в приложении. Таким образом, ваш выбор состоит в том, чтобы поместить эти знания в класс Factory, как выразились некоторые другие ответы, или отделить решение о том, какая реализация создается от того, как это создано. Кажется, что есть возможность сканировать файловую систему и получать из нее <=> файлы. Этот код будет (или может) знать тип из <=> (ответ на что ), который должен быть создан.

Независимо от того, будет ли это реализовано как <=> или как-то более оригинально (один из случаев, когда было бы неплохо управлять таблицами), решать только вам. Если это то, что может быть полезным, я могу остановиться на этом здесь.

Кстати, если базовое поведение <=> необходимо расширить или изменить для некоторых подклассов, то здесь <=> может оказаться полезным.

<Ч>

Оригинальный ответ:

Вы можете рассмотреть Decorator или Шаблон Разработка шаблонов в качестве альтернативы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top