#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
тоже будет работать.