Pull to refresh

Comments 52

если данный мой перевод сделал что-то полезное хотя бы одному человеку, то время мной было потрачено не зря ;)
Было бы здорово читать переводы таких статей. Вроде простая вещь, и не понятно как работал без этого раньше. Спасибо большое. Продолжайте!
Да, такой код действительно и легко читается, но вот создавать билдера, точнее изменять, с изменениями основного класса будет уже лениво. А вообще похоже на монодинаический код.
при изменении основного класса все-равно надо будет добавлять или новый конструктор(при использовании Telescoping Constructor паттерн) или добавлять новый сеттер (при использовании JavaBeans паттерн). поэтому добавления нового метода в билдер и изменение конструктора основного класса не особо лениво будет выглядить.
UFO just landed and posted this here
Если ваш класс требует большого кол-во аргументов в конструкторе, то это — сигнал к рефакторингу.
ну можно воспринимать данный подход как рефакторинг к шаблону Билдер
Этот шаблон не избавит нас от плохо спроектированного класса, а лишь скроет проблему.
А если у нас бин, отражающий некую сущность предметной области, с большим количеством атрибутов (как следствие класс ее моделирующий имеет множество полей), то что тогда делать? Как следует отрефакторить?
А ведь такое действительно бывает, хотя случай и немного экзотический. У нас в проекте есть классы с несколькими десятками полей. И не отделить никак.
Можно конкретный пример?
Легко, у нас, например, у User`a более десятка полей контактной информации (на этапе регистрации заполнить достаточно три — мыло, пароль, last name, это чтоб не пугать :)).

Хотя, конечно, user, под данный шаблон совсем не подходит, но вы ведь просили пример сущности с большим кол-вом полей :)
Ну так можно все же пример, который подходит под данный шаблон? Безусловно, такие примеры найти можно. Вопрос в том, насколько это часто встречается.
Пример:
есть вспомогательная форма с деревом объектов в нем. Входящие параметры на форму: параметры, по которым будет фильтроваться дерево. Как правило, их количество около 4-5. И в зависимости от них надо формировать запрос на построение дерева.
У нас в проекте таких много.

Буквально вчера в очередной раз создавал «телескоп» и тихо матерился про себя, что в двух вызовах приходится проставлять null вместо параметров «в середине».
Либо я не правильно вас понял, либо вопрос «что мешает использовать setXXX?» будет вполне уместным.

Лично для себя я вывел правило: если где-то приходится использовать null, то с большой степенью вероятности код написан плохо.
Плохо поняли. Не бину, а форме. Форма уже рулит произвольным количеством бинов, задавая входящие параметры для запросов для получения этих бинов.

Детализация примера: есть форма построения иерархии фирма — продукт. Есть два параметра: тип фирм, тип продуктов. В зависимости от контекста должны фильтроваться один из или обе сущности сразу. В зависимости от того, что из параметров не null (или не любой другой фейк), создается соответствующий whereClause. Если фейк — не фильтруем сущности.
Так понятнее?

Если правило работает для вас, рад за вас. =) Только не надо так явно спускать его мне. С тем же успехом можно сделать правило, что там, где 0 — плохо. Потому что деление на ноль.
В любом случае каждый инструмент предназначен для решения своей задачи. И если инструмент позволяет скрыть плохо спроектированный код за абстракцией, то это не значит что код становится лучше.

Если честно, все равно не понял. Предлагаю на этом закончить:)

ЗЫ: А правило это я для себя вывел, т.к. не очевидно является ли для метода параметр 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);
остальные параметры выставляем сеттерами.

Выйгрышь получается только в синтаксисе? Зато не надо класс дополнительный создавать.
Выигрыш в ленивой инициализации класса. То есть можно иметь как бы частично инициализированный класс и из него (типа-шаблона) создавать уже готовые инстансы.
Нужно, например, когда в цикле выдаём почти одинаковые объекты.
прочитайте в статье про недостатки JavaBeans паттерна
А. Я несколько невнимательно отнесся к тому когда вызывается build у паттерна builder. Тогда да — вопрос снят.
Но в чем плюс перед следующим кодом:

Нууу, как бы вам сказать… Примерно в том, что ваш код не скомпилится). У вас дополнительные параметры объявлены как final и не сетятся в конструкторе. Компилятор ругнется на variable might not have been initialized.
я просто не пишу на java. Что я имел ввиду в коде я думаю понятно)
Действительно красивое решение, но вот думаю на сколько будет дружить например с 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/).
ГоФ про билдер пишет «отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться различные представления»

Паттерн композитор «компонует объекты в древовидные структуры для представления иерархий часть-целое. Позволяет клиентам единообразно трактовать индивидуальные и составные объекты» (ГоФ)
Ну вот не builder здесь описан, ну не похож. Самое интересное, что ссылки все дают на верное описание. Отделением создания объекта от его представления занимаются все порождающие паттерны.
Спасиб, теперь я знаю грамотное название паттерна на который я подумал. :)
А нельзя просто из всех сеттеров возвращять this?
Можно, но во-первых это не хорошо, когда сеттер что-то возвращает — он же сеттер. Во-вторых это не делает объект не изменяемым, в отличии от билдера.
Как раз начал читать эту книгу на английском. Перевод классный, как в прочем и книга. Однозначно рекомендую к прочтению целиком.
спасибо, что оценили перевод, старался.

а книга действительно must read, но только для практика, а не для человека только изучающего java.
Как альтернативу можно предложить передавать в конструктор HashMap с параметрами, а уже внутри него вызывать get/set'еры. Обязательные параметры в этом случае можно передавать отдельно от HashMap (если их мало), либо вместе с HashMap и мониторить их наличие в конструкторе (при отсутствии кидать Exception).
Проблема в том что HashMap не говорит какие свойства нужно про инициализировать в нем что бы корректно создать объект. Поэтому приходится подробно описывать в документации структуру HashMap.
Документировать имхо нужно в любом случае всё и вся.
Предлагаю улучшения к статье, чтобы пример стал более жизненным:
  • Для рассматриваемого класса NutritionFacts логично предусмотреть getter'ы для неизменяемых полей. Иначе получается, что объект-то мы построим, а воспользоваться им не сможем.
  • Поля Builder'а логичнее именовать в соответствием с конвенцией JavaBeans, а именно setXXX(). Поскольку данный способ является уже стандартом де-факто для Java, подобный подход улучшит читабельность кода.
согласен, спасибо за замечание
setXXX() — это стандарт для JavaBeans, а во fluent interface префикс set чаще опускают, чем оставляют.
Конструктор 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);

призвано решать данную проблему
synchronize это дополнительные расходы
Было бы здорово, если бы Вы упомянули об этом в статье.
Sign up to leave a comment.

Articles