Шаблон построителя и объект конфигурации
Шаблон построителя популярен для создания неизменяемых объектов, но есть некоторые накладные расходы на программирование для создания построителя. Поэтому мне интересно, почему бы просто не использовать объект конфигурации.
Использование компоновщика будет выглядеть так:
Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build();
Очевидно, что это очень читабельно и кратко, но вы должны реализовать компоновщик:
public class Product {
public final String name;
public final float alcohol;
public final float size;
public final float price;
private Product(Builder builder) {
this.name = builder.name;
this.alcohol = builder.alcohol;
this.size = builder.size;
this.price = builder.price;
}
public static class Builder {
private String name;
private float alcohol;
private float size;
private float price;
// mandatory
public static Builder name(String name) {
Builder b = new Builder();
b.name = name;
return b;
}
public Builder alcohol(float alcohol) {
this.alcohol = alcohol;
return.this;
}
public Builder size(float size) {
this.size = size;
return.this;
}
public Builder price(float price) {
this.price = price;
return.this;
}
public Product build() {
return new Product(this);
}
}
}
Моя идея состоит в том, чтобы уменьшить код с помощью простого объекта конфигурации, как это:
class ProductConfig {
public String name;
public float alcohol;
public float size;
public float price;
// name is still mandatory
public ProductConfig(String name) {
this.name = name;
}
}
public class Product {
public final String name;
public final float alcohol;
public final float size;
public final float price;
public Product(ProductConfig config) {
this.name = config.name;
this.alcohol = config.alcohol;
this.size = config.size;
this.price = config.price;
}
}
Использование:
ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);
Для этого использования требуется еще несколько строк, но он также очень удобочитаем, но реализация намного проще, и, возможно, его будет легче понять тем, кто не знаком с шаблоном компоновщика. Кстати: есть ли название для этого шаблона?
Есть ли недостаток в подходе конфигурации, который я пропустил?
9 ответов
Шаблон компоновщика улучшает разделение - ваш продукт может быть интерфейсом, и единственный класс, который знает о реализации (или реализациях, в некоторых случаях), это компоновщик. Если разработчик также реализует интерфейс, вы можете добавить его в свой код, чтобы еще больше увеличить развязку.
Такое разделение означает, что ваш код более удобен в обслуживании и его проще тестировать.
Как вы уже указали, вы теряете несколько преимуществ шаблона компоновщика (новый уродлив и сложнее в обслуживании, а детали протекают по сравнению с чистым компоновщиком).
Однако мне больше всего не хватает того, что шаблон компоновщика можно использовать для предоставления так называемых "плавных интерфейсов".
Вместо этого:
ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);
Ты можешь сделать:
ProductFactory.create()
.drink("Vodka")
.whereAlcohoolLevelIs(0.38)
.inABottleSized(0.7)
.pricedAt(17.99)
.build();
Не всем нравятся свободно распространяемые интерфейсы, но они определенно очень хорошо используют шаблон построителя (во всех свободно распространяемых интерфейсах должен использоваться шаблон построителя, но не все шаблоны построителя являются свободными интерфейсами).
В некоторых замечательных коллекциях Java, таких как коллекции Google, используются как очень либеральные, так и очень хорошие "плавные интерфейсы". Я бы выбрал их в любой день по сравнению с вашим подходом "проще вводить / меньше символов":)
Шаблон конфигурации и шаблон компоновщика функционально эквивалентны. Они оба решают одни и те же проблемы -
Устранить необходимость нескольких подписей конструктора
Разрешить устанавливать поля только во время строительства
Разрешить потребителям устанавливать только те значения, которые им нужны, и иметь логические значения по умолчанию для других значений.
Все, что вы хотите сделать в одном из этих шаблонов, вы можете сделать в другом, например, разрешить установку состояния только с помощью методов, выполняющих проверку, и установку состояния с помощью инкапсулированной логики. Единственная реальная разница, если вам нравится создавать объекты с new
ключевой термин или, если вам нравится звонить .build()
метод.
Какую проблему вы пытаетесь решить с помощью вашего шаблона? Шаблон компоновщика используется для объектов со многими (необязательными) параметрами, чтобы предотвратить множество различных конструкторов или очень длинные. Он также поддерживает ваш объект в согласованном состоянии (по сравнению с шаблоном javabean) во время строительства.
Разница между компоновщиком и "объектом конфигурации" (похоже на хорошее имя) заключается в том, что вам все равно придется создавать объект с теми же параметрами с помощью конструктора или метода получения / установки. Это а) не решает проблему конструктора или б) держит объект конфигурации в несогласованном состоянии. Несогласованные состояния объекта конфигурации на самом деле не повреждают его, но вы можете передать незаконченный объект конфигурации в качестве параметра. [Ссылки Михида на фантомные типы, похоже, решают эту проблему, но это опять же убивает читабельность ( new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>
вроде отстой).] В этом большое преимущество шаблона компоновщика: вы можете проверить свои параметры перед созданием объекта и вернуть любой подтип (действует как фабрика).
Объекты Config действительны для наборов параметров, которые являются обязательными. Я видел этот шаблон много раз в.NET или Java раньше.
Вы рассматривали возможность использования builder-builder?
Я думаю, что строитель (с такими префиксами, как "С") читает более естественно / свободно.
Я лично чувствую, что шаблон с первого взгляда предлагает вам более чистый код, в котором эти объекты фактически используются. С другой стороны, отсутствие получателей / установщиков не очень пригодится многим фреймворкам, которые ожидают получение / установщик верблюжьих ящиков. Это серьезный недостаток, я чувствую.
Что мне также нравится в методах получения / установки, так это то, что вы четко видите, что делаете: получаете или устанавливаете. Я чувствую, что со строителем я теряю немного интуитивной ясности здесь.
Я знаю, что многие люди читали определенную книгу, и что теперь модель строителя внезапно наслаждалась ажиотажем, как будто это был новый iPhone. Тем не менее, я не ранний усыновитель. Я использую "новый способ" только тогда, когда он действительно экономит время на любой территории, будь то производительность, обслуживание, кодирование...
Мой практический опыт состоит в том, что я обычно лучше с геттерами / сеттерами и конструкторами. Это позволяет мне повторно использовать эти POJO для любых целей.
Несмотря на то, что я вижу назначение вашего объекта конфигурации, я также думаю, что это даже больше, чем сборщик, и для чего? Что не так с сеттерами?
Может быть, нам нужно изобрести предложение WITH: например, скажем, у вас есть
public Class FooBar() {
private String foo;
public void setFoo(String bar) {
this.foo = bar;
}
public String getFoo() {
return this.foo;
}
}
public static void main(String []args) {
FooBar fuBar = new FooBar();
String myBar;
with fuBar {
setFoo("bar");
myBar = getFoo();
}
}
Ах, я не знаю... Я думаю, что это может привести к более быстрому написанию кода без всяких внутренних хлопот. Есть ли у кого-нибудь связь с гуру Oracle Java?
Это выглядит не так чисто, как использование объекта со строителем, но вы экономите время конструктора. И вы все еще можете использовать этот класс как обычный pojo / bean, который можно использовать в фреймворках...
Ребята, вам действительно понравится этот пункт, или вы думаете, что он скорее отстой? ура
IMO, шаблон компоновщика намного более надежен, если у вас есть такие вещи, как проверка и т. Д.
Шаблон компоновщика в вашем случае можно изменить, сделав следующее:
Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();
build()
Метод может выполнить все проверки, необходимые для вашего строителя. У шаблона компоновщика также есть несколько конструктивных особенностей (все из которых могут быть неприменимы в вашем случае). Для деталей проверьте этот вопрос
Основным недостатком является то, что это не в книге Джошуа, поэтому дроны не могут обернуть голову вокруг этого.
Вы используете простой объект-значение для хранения нескольких аргументов, в которых нуждается функция (/method/constructor), в этом нет ничего плохого, это было сделано целую вечность. Пока у нас нет названных необязательных параметров, нам придется придумывать обходные пути, подобные этому - это позор, а не какие-то чертовски блестящие изобретения от богов на Солнце.
Реальная разница в том, что вы выставляете поля напрямую. У Джошуа никогда не было бы открытого изменчивого поля - но он пишет API-интерфейсы, которые будут использоваться миллионами людей, большинство из которых - дебилы, и API-интерфейсы должны быть безопасными для развития в течение десятилетий, и они могут выделить много человеко-месяцев только для разработки простой класс
Кто мы, чтобы подражать этому?
Вы не должны использовать открытое поле, но защищенное или личное. Тогда для доступа вы должны использовать геттеры и сеттеры, чтобы сохранить инкапсуляцию.