Шаблон компоновщика и объект конфигурации
Шаблон builder популярен для создания неизменяемых объектов,но есть некоторые накладные расходы на программирование для создания builder. Поэтому я задаюсь вопросом, почему бы просто не использовать объект config.
Использование конструктора будет выглядеть следующим образом:
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);
Это использование требует еще несколько строк, но также очень читабельно, но реализация намного проще, и, возможно, это легче понять для тех, кто не знаком с шаблоном builder. Кстати, есть ли название для этого паттерна?
Есть ли недостаток в подходе конфигурации, который я упустил из виду?
9 ответов:
Шаблон builder улучшает развязку-ваш продукт может быть интерфейсом, и единственным классом, который знает о реализации (или реализациях, в некоторых случаях), является builder. Если конструктор также реализует интерфейс, то вы можете ввести его в свой код, чтобы еще больше увеличить развязку.
Эта развязка означает, что ваш код более доступен для обслуживания и легче тестируется.
Вы теряете несколько преимуществ шаблона строителя, как уже было указано ( новый уродлив и труднее поддерживать и пропускать детали по сравнению с чистым строителем).
Однако больше всего мне не хватает того, что шаблон builder можно использовать для обеспечения того, что называется "плавными интерфейсами".Вместо этого:
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();
Не всем нравятся свободные интерфейсы, но они определенно очень хорошо используют шаблон builder (все свободные интерфейсы должны использовать шаблон builder, но не все шаблоны builder являются свободными интерфейсами).
Некоторые большие коллекции Java, такие как коллекции Google, делают и очень либеральное и очень хорошее использование "плавных интерфейсов". Я бы выбрал их в любой день над вашим"более легким для ввода/меньшим количеством символов" подходом :)
Какую проблему вы пытаетесь решить с помощью вашего шаблона? Шаблон builder используется для объектов со многими (необязательными) параметрами, чтобы предотвратить множество различных конструкторов или очень длинных. Он также поддерживает объект в согласованном состоянии (по сравнению с шаблоном javabean) во время построения.
Разница между builder и "config object" (кажется, хорошее название) заключается в том, что вам все еще нужно создать объект с теми же парами конструктором или геттером/сеттером. Это а) не делает решить проблему конструктора или b) сохраняет объект config в несогласованном состоянии. Несогласованные состояния объекта config действительно не повредят ему, но вы можете передать незавершенный объект config как param. [Michids ссылка на фантомные типы, кажется, решает эту проблему, но это снова убивает читабельность (
new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>
своего рода отстой).] это большое преимущество шаблона builder: вы можете проверить свои параметры перед созданием объекта и Вы можете вернуть любой подтип (действует как завод).Объекты конфигурации действительны для наборов параметров, которые являются обязательными. Я видел этот шаблон много раз в .NET или java раньше.
ИМО, шаблон builder гораздо более робастен, если у вас есть такие вещи, как проверка и т. д.
Шаблон компоновщика в вашем случае можно изменить следующим образом:
Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();
Метод
build()
может выполнять все проверки, необходимые для вашего конструктора. Шаблон builder также имеет несколько преимуществ дизайна (все они могут быть неприменимы в вашем случае). Для deatils проверьте этот Вопрос
Вы рассматривали возможность использования builder-builder ?
Я действительно думаю, что строитель (с префиксами, такими как "With") читает более естественно/свободно.
Я лично чувствую, что шаблон builder на первый взгляд предлагает вам более чистый код, где эти объекты фактически используются. С другой стороны, отсутствие геттеров / сеттеров не будет очень полезно для многих фреймворков, которые ожидают геттеров/сеттеров camel case. Я чувствую, что это серьезное отступление.
Что мне также нравится с геттерами / сеттерами, так это то, что вы четко видите, что вы делаете: получаете или устанавливаете. Я чувствую, что со строителем я теряю немного интуитивной ясности здесь.
Я знаю, что многие люди прочитали конкретную книгу, и теперь внезапно модель builder получила удовольствие от шумихи, как будто это был новый iPhone. Однако я не являюсь ранним усыновителем. Я использую "новый способ" только тогда, когда он действительно действительно доказывает большую экономию времени на любой территории, будь то производительность, обслуживание, кодирование...
Мой практический опыт заключается в том, что я обычно лучше справляюсь с геттерами/сеттерами и конструкторами. Это позволяет мне использовать эти POJO'S для любых целей.Хотя я вижу, что назначение вашего объекта конфигурации, я также думаю, что это даже больше накладных расходов, чем у строителя, и для чего? Что не так с сеттерами?
Может быть, нам нужно изобрести предложение 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, который можно использовать в фреймворках...
Вам, ребята, действительно нравится этот пункт или вы думаете, что он будет скорее отстойным? Ура
Шаблон конфигурации и шаблон компоновщика функционально эквивалентны. Они оба решают одни и те же задачи -
Устраните необходимость в нескольких сигнатурах конструктора
Разрешить устанавливать поля только во время построения
Разрешить потребителям устанавливать только те значения, которые их интересуют, и иметь логические значения по умолчанию для других значений
Все, что вы хотите сделать в одном из этих паттернов, вы можете сделать в другом, например, только разрешение устанавливать состояние с помощью методов, выполняющих проверку, и устанавливать состояние с помощью инкапсулированной логики. Единственное реальное различие заключается в том, нравится ли вам создавать объекты с ключевым термином
new
или вызывать метод.build()
.
Главный недостаток состоит в том, что его нет в книге Джошуа, поэтому дроны не могут обернуть вокруг него свои головы.
Вы используете простой объект value для хранения нескольких аргументов, необходимых функции(/method/constructor), в этом нет ничего плохого, это было сделано в течение многих веков. До тех пор, пока мы не назовем необязательные параметры, нам придется изобретать обходные пути, подобные этому - это позор, а не какие-то чертовски блестящие изобретения богов на Солнце.
Реальная разница в том, что вы выставляйте поля напрямую. Джошуа никогда не будет иметь публичного изменяемого поля - но он пишет API, которые будут использоваться миллионами людей, большинство из которых являются дебилами, и API должны быть безопасны для эволюции в течение десятилетий, и они могут выделить много человеко-месяцев только для разработки простого класса
Кто мы такие, чтобы подражать этому?