Comments 52
Круто.
Да, такой код действительно и легко читается, но вот создавать билдера, точнее изменять, с изменениями основного класса будет уже лениво. А вообще похоже на монодинаический код.
при изменении основного класса все-равно надо будет добавлять или новый конструктор(при использовании Telescoping Constructor паттерн) или добавлять новый сеттер (при использовании JavaBeans паттерн). поэтому добавления нового метода в билдер и изменение конструктора основного класса не особо лениво будет выглядить.
UFO just landed and posted this here
Если ваш класс требует большого кол-во аргументов в конструкторе, то это — сигнал к рефакторингу.
ну можно воспринимать данный подход как рефакторинг к шаблону Билдер
Этот шаблон не избавит нас от плохо спроектированного класса, а лишь скроет проблему.
А если у нас бин, отражающий некую сущность предметной области, с большим количеством атрибутов (как следствие класс ее моделирующий имеет множество полей), то что тогда делать? Как следует отрефакторить?
А ведь такое действительно бывает, хотя случай и немного экзотический. У нас в проекте есть классы с несколькими десятками полей. И не отделить никак.
Можно конкретный пример?
Легко, у нас, например, у User`a более десятка полей контактной информации (на этапе регистрации заполнить достаточно три — мыло, пароль, last name, это чтоб не пугать :)).
Хотя, конечно, user, под данный шаблон совсем не подходит, но вы ведь просили пример сущности с большим кол-вом полей :)
Хотя, конечно, user, под данный шаблон совсем не подходит, но вы ведь просили пример сущности с большим кол-вом полей :)
Ну так можно все же пример, который подходит под данный шаблон? Безусловно, такие примеры найти можно. Вопрос в том, насколько это часто встречается.
Пример:
есть вспомогательная форма с деревом объектов в нем. Входящие параметры на форму: параметры, по которым будет фильтроваться дерево. Как правило, их количество около 4-5. И в зависимости от них надо формировать запрос на построение дерева.
У нас в проекте таких много.
Буквально вчера в очередной раз создавал «телескоп» и тихо матерился про себя, что в двух вызовах приходится проставлять null вместо параметров «в середине».
есть вспомогательная форма с деревом объектов в нем. Входящие параметры на форму: параметры, по которым будет фильтроваться дерево. Как правило, их количество около 4-5. И в зависимости от них надо формировать запрос на построение дерева.
У нас в проекте таких много.
Буквально вчера в очередной раз создавал «телескоп» и тихо матерился про себя, что в двух вызовах приходится проставлять null вместо параметров «в середине».
Либо я не правильно вас понял, либо вопрос «что мешает использовать setXXX?» будет вполне уместным.
Лично для себя я вывел правило: если где-то приходится использовать null, то с большой степенью вероятности код написан плохо.
Лично для себя я вывел правило: если где-то приходится использовать null, то с большой степенью вероятности код написан плохо.
Плохо поняли. Не бину, а форме. Форма уже рулит произвольным количеством бинов, задавая входящие параметры для запросов для получения этих бинов.
Детализация примера: есть форма построения иерархии фирма — продукт. Есть два параметра: тип фирм, тип продуктов. В зависимости от контекста должны фильтроваться один из или обе сущности сразу. В зависимости от того, что из параметров не null (или не любой другой фейк), создается соответствующий whereClause. Если фейк — не фильтруем сущности.
Так понятнее?
Если правило работает для вас, рад за вас. =) Только не надо так явно спускать его мне. С тем же успехом можно сделать правило, что там, где 0 — плохо. Потому что деление на ноль.
Детализация примера: есть форма построения иерархии фирма — продукт. Есть два параметра: тип фирм, тип продуктов. В зависимости от контекста должны фильтроваться один из или обе сущности сразу. В зависимости от того, что из параметров не null (или не любой другой фейк), создается соответствующий whereClause. Если фейк — не фильтруем сущности.
Так понятнее?
Если правило работает для вас, рад за вас. =) Только не надо так явно спускать его мне. С тем же успехом можно сделать правило, что там, где 0 — плохо. Потому что деление на ноль.
В любом случае каждый инструмент предназначен для решения своей задачи. И если инструмент позволяет скрыть плохо спроектированный код за абстракцией, то это не значит что код становится лучше.
Если честно, все равно не понял. Предлагаю на этом закончить:)
ЗЫ: А правило это я для себя вывел, т.к. не очевидно является ли для метода параметр null допустимым или нет. Приходится писать документацию, которая отнюдь не обязательно избавит от ошибок. А раз что-то не очевидно из кода, то код плох. Примерно так.
Если честно, все равно не понял. Предлагаю на этом закончить:)
ЗЫ: А правило это я для себя вывел, т.к. не очевидно является ли для метода параметр null допустимым или нет. Приходится писать документацию, которая отнюдь не обязательно избавит от ошибок. А раз что-то не очевидно из кода, то код плох. Примерно так.
Вах, век живи — век учись. Красивый паттерн.
Интересная идея. Как раз думаю как мне лучше передать кучу параметров в граф.
Но в чем плюс перед следующим кодом:
Но в чем плюс перед следующим кодом:
// Telescoping constructor pattern — плохо масштабируемый!
public class NutritionFacts {
private final int servingSize; // обязательный параметр
private final int servings; // обязательный параметр
private final int calories; // дополнительный параметр
private final int fat; // дополнительный параметр
private final int sodium; // дополнительный параметр
private final int carbohydrate; // дополнительный параметр
public NutritionFacts(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
//+сеттеры
}
блин, отправилось раньше времени.
используем этот код тогда так:
NutritionFacts cocaCola = new NutritionFacts(240, 8);
остальные параметры выставляем сеттерами.
Выйгрышь получается только в синтаксисе? Зато не надо класс дополнительный создавать.
используем этот код тогда так:
NutritionFacts cocaCola = new NutritionFacts(240, 8);
остальные параметры выставляем сеттерами.
Выйгрышь получается только в синтаксисе? Зато не надо класс дополнительный создавать.
прочитайте в статье про недостатки JavaBeans паттерна
Но в чем плюс перед следующим кодом:
Нууу, как бы вам сказать… Примерно в том, что ваш код не скомпилится). У вас дополнительные параметры объявлены как final и не сетятся в конструкторе. Компилятор ругнется на variable might not have been initialized.
Действительно красивое решение, но вот думаю на сколько будет дружить например с Hibernate, который ожидает POJO? хотя все get/set объявить private, вроде должно работать
Ну для hibernate можно просто аннотации на поля вешать, да и POJO это не есть JavaBean, это просто объект, данный термин появился во время массового сруливания с EJB 2х. Где вместо текущих POJO были так называемые EntityBean's.
А в случае с аннотациями наличие get/set не обязательно? (у меня почему то отложилось в памяти, что они все таки нужны).
Сдесь в прицыпе не важно с аннотациями или без, я просто с XML со 2-го гибера не работал, но и там можно было определить доступ default-access=«field|property». Просто когда на аннотациях делается маппинг, если аннотация PK'ея ставится на поле, тогда гибер будет работать не через методы, а обращатся к полям. Для обратного эффекта ставим аннотацию на метод.
Если мне не изменяет память в нотации GoF данный паттерн называется Composite en.wikipedia.org/wiki/Composite_pattern, а Builder это совсем другой паттерн. Да кстати и ссылку вы даете на описание Builder'а так как он описан у GoF. Поправте если не прав.
Builder это именно тот паттерн, что используется (здесь можно почитать на русском codelab.ru/pattern/builder/).
ГоФ про билдер пишет «отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться различные представления»
Паттерн композитор «компонует объекты в древовидные структуры для представления иерархий часть-целое. Позволяет клиентам единообразно трактовать индивидуальные и составные объекты» (ГоФ)
ГоФ про билдер пишет «отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться различные представления»
Паттерн композитор «компонует объекты в древовидные структуры для представления иерархий часть-целое. Позволяет клиентам единообразно трактовать индивидуальные и составные объекты» (ГоФ)
см. также «Fluent Interfaces»: en.wikipedia.org/wiki/Fluent_interface
А нельзя просто из всех сеттеров возвращять this?
Как раз начал читать эту книгу на английском. Перевод классный, как в прочем и книга. Однозначно рекомендую к прочтению целиком.
Как альтернативу можно предложить передавать в конструктор HashMap с параметрами, а уже внутри него вызывать get/set'еры. Обязательные параметры в этом случае можно передавать отдельно от HashMap (если их мало), либо вместе с HashMap и мониторить их наличие в конструкторе (при отсутствии кидать Exception).
Предлагаю улучшения к статье, чтобы пример стал более жизненным:
- Для рассматриваемого класса NutritionFacts логично предусмотреть getter'ы для неизменяемых полей. Иначе получается, что объект-то мы построим, а воспользоваться им не сможем.
- Поля Builder'а логичнее именовать в соответствием с конвенцией JavaBeans, а именно setXXX(). Поскольку данный способ является уже стандартом де-факто для Java, подобный подход улучшит читабельность кода.
Конструктор private NutritionFacts(Builder builder) для лёгкой экономии можно сделать не private, а с доступом по умолчанию (package), иначе будет автоматически сгенерирован ещё один невидимый «синтетический» конструктор. Ещё как вариант вместо явного вызова new NutritionFacts.Builder() можно сделать статический метод-фабрику.
спасибо, всё просто и понятно объяснили.
мне паттерн понравился.
мне паттерн понравился.
Также JavaBeans паттерн исключает возможность сделать класс неизменным(immutable), что требует дополнительных усилий со стороны программиста для обеспечения безопасности в многопоточной среде
По-моему, использование synchronize на блоке кода:
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
призвано решать данную проблему
Sign up to leave a comment.
Использование паттерна Builder в случае, когда мы сталкиваемся с конструктором с многими параметрами