Если вы планируете использовать шаблон построителя, то после разделения задач я бы предпочел использовать отдельный класс для объекта BusinessObject
, используя объект шаблона BusinessObjectBuilder
построителя. Чтобы получить доступ к объекту шаблона компоновщика из соответствующего домена / бизнес-объекта, вы можете (необязательно, и я бы порекомендовал при необходимости) добавить метод public static create()
для создания экземпляра как объекта компоновщика, так и инкапсулированного бизнес-объекта для компоновки. , Лично я предпочитаю свободный стиль объектов-строителей, так как методы могут быть объединены в цепочку, и это значительно облегчает написание кода.
Поскольку вас беспокоит сложность построения поля Team1Object и поля Team2Object как отдельных В связи с этим я бы подумал, что вы ищете не плоский строительный шаблон, а фасады строительного шаблона или фасады строителей. Чтобы использовать фасады компоновщика, вы должны использовать общий базовый класс компоновщика и классы фасада компоновщика, производные от базового класса.
Базовый класс при реализации создаст простой BusinessObject и предоставит метод для сборки каждая область, в том числе, путем включения бегущих строителей фасадов. Свободные строители фасадов будут строить только одну часть участка объекта, часть которого может быть сложной сама по себе и, следовательно, может представлять собой отдельную проблему от общего строительства объекта в целом.
Как во всех классах Fluent Builder тип возврата такой же, как и в классе Fluent Builder (или Fluent Builder). Рассмотрим следующие модификации:
public class BusinessObject {
internal BusinessObject() {
// Default constructor should exist
// but only needs to be visible at the
// BusinessObjectBuilder scope.
// use whatever access modifier you would
// prefer, however, based on needs,
// internal or public is appropriate.
// in C++, use `friend BusinessObjectBuilder`
}
public Team1Object object1;
public Team2Object object2;
public String debug;
...
public static BusinessObjectBuilder create() {
return new BusinessObjectBuilder();
}
}
public class BusinessObjectBuilder {
protected BusinessObject bObject; // the object we will build
internal BusinessObjectBuilder() {
// A default constructor, again minimally visible
// to BusinessObject; internal or public is good here.
// Needs to create a basic BusinessObject.
bObject = new BusinessObject();
}
public BusinessObjectBuilder debug(String debugString) {
// Sets BusinessObject.debug
this.bObject.debug += debugString + "\n";
// Then returns the BusinessObjectBuilder.
// Every method should return this except the facade methods
// and the build method.
return this;
}
public Team1ObjectBuilder team1Object() {
// Returns the Team1Object builder facet.
return new Team1ObjectBuilder(this.bObject);
}
public Team2ObjectBuilder team2Object() {
// Returns the Team2Object builder facet.
return new Team1ObjectBuilder(this.bObject);
}
public BusinessObject build() {
// Technically, it's already built at this point. Return it.
return this.bObject;
}
}
public class Team1ObjectBuilder extends BusinessObjectBuilder {
private BusinessObject bObject; // the object we will build
internal Team1ObjectBuilder(BusinessObject bObject) {
// This time we copy the object we were building
this.bObject = bObject;
}
private Team1Object somehowComputeObject1() {
// pour on the magic
return new Team1Object();
}
public Team1ObjectBuilder append(Context someApplicationContext) {
this.bObject.object1 = somehowComputeObject1();
}
}
public class Team2ObjectBuilder extends BusinessObjectBuilder {
private BusinessObject bObject; // the object we will build
internal Team2ObjectBuilder(BusinessObject bObject) {
// Again we copy the object we were building
this.bObject = bObject;
}
private Team2Object somehowComputeObject2() {
// pour on the magic
return new Team2Object();
}
public Team2ObjectBuilder append(Context someApplicationContext) {
this.bObject.object2 = somehowComputeObject2();
}
}
Если вы используете этого бегущего строителя с беглым рисунком фасада, вы можете использовать его так:
BusinessObject complexBusinessObject = BusinessObject.create()
.debug("Let's build team1Object.")
.team1Object().append( /* someApplicationContext */)
.debug("Let's build team2Object.")
.team2Object().append( /* someApplicationContext */)
.debug("All done.")
.build();
Но тогда я не уверен если это то, чего вы хотели достичь, особенно потому, что я не очень хорошо знаком с объектами Team1 и Team2 или с тем, как вы могли бы определить их с точки зрения обязанностей и иерархии.
Вы упомянули цепочку ответственности. Этот шаблон используется, когда цепочка компонентов получает по очереди (в цепочке) для обработки команды / запроса и при необходимости останавливает выполнение цепочки.
Рассмотрим такой процесс, как наем сотрудника. Есть несколько процессов на этом пути. По завершении каждого процесса начинается следующий процесс в цепочке. Если возникает исключение, возможно, сотрудник все-таки не принят на работу (остановка цепочки).
Для этого у нас есть цепочка обязанностей, и мы будем использовать шаблон проектирования цепочки ответственности. Если, например, процессы Team2 зависят от процессов Team1, вы можете использовать этот шаблон, поскольку он решит эту проблему.
Чтобы использовать шаблон цепочки ответственности, вам понадобятся BusinessObject
, а также один или несколько BusinessObjectModifier
классов. Поскольку область действия здесь ограничена объектами Team1Appender
и Team2Appender
, мы будем использовать эти два в качестве ссылки.
Чтобы построить цепочку, вы можете захотеть использовать базовый класс для * Поле 1030 * для следующего звена в цепочке и метод add()
для передачи к следующему ответственному звену в цепочке.
Рассмотрим следующий шаблон цепочки ответственности:
public class BusinessObject {
public Team1Object object1;
public Team2Object object2;
public String debug;
...
}
public abstract class BusinessObjectAppender { // provides shared append() modifier
protected BusinessObjectAppender next = null;
public void add(BusinessObjectAppender boa) {
if (this.next == null) {
this.next = boa;
}
else {
next.add(boa); // recursive call to the end of the linked list "chain"
}
}
public abstract void append(BusinessObject businessObject, Context someApplicationContext);
}
public class Team1ObjectAppender extends BusinessObjectAppender {
public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
Team1Object object1 = somehowComputeObject1();
businessObject.object1 = object1;
if (this.next == null) {
return businessObject; // you have to since you can't pass by ref/out in java
}
else {
return next.append(businessObject, someApplicationContext);
}
}
}
public class Team2ObjectAppender extends BusinessObjectAppender {
public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
Team2Object object2 = somehowComputeObject2();
businessObject.object2 = object2;
if (this.next == null) {
return businessObject; // you have to since you can't pass by ref/out in java
}
else {
return next.append(businessObject, someApplicationContext);
}
}
}
Теперь это должно настроить цепочку. Чтобы использовать его, вы можете сделать что-то вроде:
BusinessObject businessObject = new BusinessObject();
BusinessObjectAppender appendChain = new Team1ObjectAppender();
appendChain.add(new Team2ObjectAppender()); // start --> team1 --> team2 --> done
businessObject = appendChain(businessObject, /*someApplicationContext*/);
Решает ли это вашу проблему? Если у вас есть цепочка ответственности, тогда, возможно.
Я вижу, что ваша оригинальная спецификация использовала строителя в качестве субъекта для передачи по цепочке вместо конечного объекта. Это интересное пересечение двух шаблонов.
Если вы хотите использовать конструктор, но затем построить объект с использованием метода цепочки ответственности, вы можете рассмотреть что-то вроде:
public class BusinessObject {
internal BusinessObject() {
// Default constructor should exist
// but only needs to be visible at the
// BusinessObjectBuilder scope.
// use whatever access modifier you would
// prefer, however, based on needs,
// internal or public is appropriate.
// in C++, use `friend BusinessObjectBuilder`
}
public Team1Object object1;
public Team2Object object2;
public String debug;
...
public static BusinessObjectBuilder create() {
return new BusinessObjectBuilder();
}
}
public abstract class BusinessObjectAppender { // provides shared append() modifier
protected BusinessObjectAppender next = null;
public void add(BusinessObjectAppender boa) {
if (this.next == null) {
this.next = boa;
}
else {
next.add(boa); // recursive call to the end of the linked list "chain"
}
}
public abstract void append(BusinessObject businessObject, Context someApplicationContext);
}
public class Team1ObjectAppender extends BusinessObjectAppender {
public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
Team1Object object1 = somehowComputeObject1();
businessObject.object1 = object1;
if (this.next == null) {
return businessObject; // you have to since you can't pass by ref/out in java
}
else {
return next.append(businessObject, someApplicationContext);
}
}
}
public class Team2ObjectAppender extends BusinessObjectAppender {
public BusinessObject append(BusinessObject businessObject, Context someApplicationContext) {
Team2Object object2 = somehowComputeObject2();
businessObject.object2 = object2;
if (this.next == null) {
return businessObject; // you have to since you can't pass by ref/out in java
}
else {
return next.append(businessObject, someApplicationContext);
}
}
}
public class BusinessObjectBuilder {
protected BusinessObject bObject; // the object we will build
internal BusinessObjectBuilder() {
// A default constructor, again minimally visible
// to BusinessObject; internal or public is good here.
// Needs to create a basic BusinessObject.
bObject = new BusinessObject();
}
public BusinessObjectBuilder debug(String debugString) {
// Sets BusinessObject.debug
this.bObject.debug += debugString + "\n";
// Then returns the BusinessObjectBuilder.
// Every method should return this except the facade methods
// and the build method.
return this;
}
public BusinessObjectBuilder append(Context someApplicationContext) {
// Create the chain
BusinessObjectAppender appendChain = new Team1ObjectAppender();
appendChain.add(new Team2ObjectAppender()); // start --> team1 --> team2 --> done
this.bObject = appendChain(this.bObject, someApplicationContext);
// Return the Builder.
return this;
}
public BusinessObject build() {
// Technically, it's already built at this point. Return it.
return this.bObject;
}
}
А затем используйте это так:
BusinessObject complexBusinessObject = BusinessObject.create()
.debug("Run through the chain of responsibilities.")
.append( /* someApplicationContext */)
.debug("All done.")
.build();
Это не единственный способ пересечь эти два понятия. Есть несколько конечных точек, где линии размываются между шаблонами, хотя я не собираюсь перечислять их все sh.
Я бы, конечно, хотел бы ответить на ваши вопросы.
- Это правильный шаблон? Это зависит от того, что вам нужно.
Цепочка ответственности состоит из источника команды (в данном случае append()
блок вызывающего), который обрабатывает команду (append
) через каждый обрабатываемый объект в пределах одиночно связанный список последовательно обрабатываемых последовательностей объектов обработки (BusinessObjectAppender
объектов).
Если у вас нет цепочки, это определенно неправильный шаблон. Если вам не требуется один источник команд (вызов append()
в одном месте), то это не обязательно правильный дизайн или его можно реорганизовать до тех пор, пока он не будет.
Шаблон Builder предоставляет решение для построения сложный объект, когда конструктор просто не вырезает его. В этом случае создание такого объекта само по себе является отдельной задачей, и поэтому конструкция отделена от класса, который он создает, и помещается в отдельный класс построителя.
Если вам нужен способ конструирования объекта, который отличается от того, как он будет представлен, это может быть правильный шаблон.
Например, способ, которым автомобиль представлен водителю, покупателю или продавцу, скорее всего, не использует те же интерфейсы, которые были использованы для создания его на заводе. Конечно, у него будут марка, модель и год, все равно. Но заказчик не беспокоится о стоимости деталей, времени, необходимом для сборки, результатах испытаний различных систем, которые были задействованы сотрудниками в дни его создания. Но, конечно же, через 6,5 секунды он становится 0-60 и окрашивается в красный цвет.
Когда построение объекта является сложным, а представление отличается от способа его построения, шаблон Builder решит его , (И выглядит хорошо, когда вы используете свободный подход.)
И шаблон строителя, и шаблон цепочки ответственности являются частью оригинальных шаблонов проектирования «Банды четырех».
Где они отличается это шаблон Builder - это шаблон Creation, а Chain of Responsibility - поведенческий шаблон.
Я не ставлю перед собой цель переписать книгу, поэтому я мог бы просто отослать вас к заголовку «Шаблоны проектирования: элементы многоразового использования». «Объектно-ориентированное программное обеспечение» (1994. Гамма, Эрих; Хелм, Ричард; Джонсон, Ральф; и Флиссидес, Джон.), Если вы хотите, чтобы один из их шаблонов соответствовал вашим собственным потребностям. Поскольку вы не объяснили цель команды 1 и команды 2, я не могу решить для вас, что лучше.
Каковы другие способы?
Банда четырех предоставила несколько других образцов творчества и поведения.
Если цепь ответственности не решит вашу проблему, тогда Шаблон команды мог бы. (Это почти то же самое, что сделал для вас абстрактный метод BusinessObjectAppender.append()
, за исключением цепочки; поскольку append()
примерно execute()
однажды реализовано.)
Если вам нужно выполнить одну и ту же Команду для той же темы через несколько (1 ... n) процессов, но если процессы не связаны друг с другом в цепочке ответственности и не требуют определенного порядка, тогда простой итератор подойдет. К счастью, Java предоставляет множество возможностей, которые очень легко повторяемы. Рассмотрим ArrayList<Appender> appenders
.
Есть много, много вариантов на выбор. Иногда вы можете смешивать их.
Я фактически выбрал класс шаблонов проектирования для Udemy, специально для C ++, но есть много мест в Интернете, где вы можете найти эту информацию. Это выглядит как хороший источник сводок, особенно потому, что примеры приведены в Java и предлагают альтернативы выбранным мною вариантам дизайна, давая вам несколько дополнительных примеров: JournalDev .
Надеемся , это помогает направить вас в правильном направлении.