Несколько фабрик внутри родительской фабрики

#java #design-patterns #abstract-factory

#java #шаблоны проектирования #аннотация-фабрика

Вопрос:

Как вы можете видеть на рисунке ниже диаграмма классов фабрики объектову меня есть шаблон factory, который создает объекты во время выполнения. Когда я изучал свою базу данных, я решил, что есть студент и профессор, которые «ЯВЛЯЮТСЯ» людьми, потому что у них есть некоторые общие поля (например, name и prename).

Аналогичным образом, я решил, что есть постоянные профессора и заместители профессора, которые «являются» профессорами.

Во время выполнения пользователь выбирает такие объекты, как «Профессор», «Студент» и т.д. Не Person. Итак, в этом случае я должен создать новые объекты Professor, Student и т.д. Аналогично, когда пользователь выбирает объект Professor, он должен решить, какой тип профессора хочет создать.

Код для фабрики, которую я создал до сих пор, похож

 public class EntityFactory {
protected String entityType;

public EntityFactory(String personType){
    this.entityType = entityType;
}

public Entity createEntity(){
    if(entityType.equalsIgnoreCase("Student")){
        return new Student();
    }
    else if(entityType.equalsIgnoreCase("Professor")){
        return new Professor();
    }
    //...
}
}
  

Мне интересно: как я могу «передать» абстрактные сущности Person и Professor для создания конкретной сущности, которую я хочу? Возможно ли это? Или мне нужно создавать разные фабрики для объектов, которые я задаю как абстрактные? И как я собираюсь связать эти фабрики с «родительской» фабрикой в таком случае?

Комментарии:

1. В чем смысл этой фабрики в первую очередь? Если я хочу создать нового Student, зачем мне вызывать type-unsafe Student s = (Student) factory.create("Student") вместо простого выполнения Student s = new Student() ?

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

3. @VassilisDe это не отвечает на вопрос @JBNizet. Где / почему Person тип необходим в первую очередь? Зачем вам нужна эта абстракция ? Если у вас есть только общие поля в Professor и Student , лучше ИМО сгруппировать их в объекты-значения многократного использования ( Address , Name и т.д.), Чем слепо использовать эту возможность для создания базового класса.

4. @guillaume31 Я не уверен, что точно понимаю, что вы подразумеваете под IMO…

5. @VassilisDe www.urbandictionary.com/define.php?term=imo 😉

Ответ №1:

Честно говоря, я не уверен, правильно ли я понимаю ваш вопрос.

Часть 1: Вы хотите «передать» создание объектов Person or Professor , поскольку они абстрактны, то есть на самом деле не существуют в базе данных и просто служат целям базовых классов.

Одна вещь, о которой я мог бы подумать, заключалась бы в том, чтобы запретить пользователю вашей фабрики запрашивать эти типы сущностей, используя enum вместо a String для идентификации entityType . Это enum будет содержать только то, что действительно может быть создано, т. е. не Person или Professor .

Часть 2: Или мне нужно создавать разные фабрики для объектов, которые я задаю как абстрактные? И как я собираюсь связать эти фабрики с «родительской» фабрикой в таком случае?

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

Способ, которым вы могли бы разделить это на несколько фабрик (или что-то подобное), заключается в использовании шаблона стратегии (http://en.wikipedia.org/wiki/Strategy_pattern ), который использует запрошенный entityType в качестве контекста для выбора стратегии для использования — стратегией может быть специализированная фабрика для этого конкретного типа.

Если ваши типы Person и Professor действительно абстрактны в смысле Java, то вы не можете создать их экземпляр, что означает, что было бы невозможно иметь родительские фабрики. Я предполагаю, что вы, вероятно, могли бы реализовать все внутри конструктора Person или использовать какой-нибудь метод инициализации.

Надеюсь, это может продвинуть вас на шаг вперед 🙂

Обновление: Первым шагом было бы использовать switch инструкцию со значениями enum в вашем методе создания

 public enum EntityType {
    STUDENT,
    ABIDING_PROF,
    DEPUTY_PROF

    // ...
}

public class EntityFactory {
    private final EntityType entityType;

    public EntityFactory(EntityType entityType) {
        this.entityType = entityType;
    }

    public Entity createEntity() {
        switch (entityType) {
            case STUDENT:
                return new Student(); // or some more sophisticated construction logic

            case DEPUTY_PROF:
                return new DeputyProfessor(); // .. or something else

            // ...
        }
    }
}
  

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

Комментарии:

1. перечисления могут подойти мне. Если я хорошо понял, что вы имеете в виду, я должен создать enum = {Student, Abiding_prof, Deputy_prof} . Я должен признать, что я мало что знаю о перечислениях … как бы мне изменить creatEentity() метод, чтобы работать с enum?

2. Я обновил свой исходный пост из-за того, что форматирование в комментариях на самом деле не работает 🙂

Ответ №2:

Мне кажется, что вам не нужна фабрика здесь в первую очередь. Думайте об этом как о стратегиях создания, основанных на заданном предварительном условии: должен существовать уровень (возможно, ваш пользовательский интерфейс), где пользователь явно указывает, чего он или она хочет. Итак, все, что вам нужно, это другая реализация () -> Entity функции (версия C # есть Entity Create() {} ). Существует два распространенных способа решения такой проблемы: либо AbstractFactory — фабрика фабрик, фабрика более высокого порядка, либо обычный Strategy шаблон (который также хорошо сочетается с State шаблоном, где фактическим состоянием является выбор, сделанный пользователем).

Неважно как, в конечном итоге вы доберетесь до () -> Entity подписи, даже если она будет явно определена в вашем коде. Похоже, что Func<Entity> or Func<Task<Entity>> является наиболее компактным, но многоразовым и тестируемым типом. Хотя пользовательский IEnitityBuilder тоже будет работать.