C
C
Clean Coder2020-11-03 12:22:03
Design patterns
Clean Coder, 2020-11-03 12:22:03

What does pattern builder do compared to regular setters?

Object object = ObjectBuilder
                .newObject()
                .withA(a)
                .withB(b)
                .build();


or

Object object = new Object();
object.setA(a);
object.setB(b);

Answer the question

In order to leave comments, you need to log in

2 answer(s)
E
Evgeny Shatunov, 2020-11-07
@HustleCoder

To get started, you should refer to some source of information about this template. For example, to this .

Imagine a complex object that requires painstaking step-by-step initialization of many fields and nested objects. The initialization code for such objects is usually hidden inside a monstrous constructor with a dozen parameters. Or even worse, it is sprayed all over the client code.

In other words, a monstrous constructor is understood as the intentional creation of a monolith . A monolith is created, as a rule, once and then goes out until the complete decomposition of its code, when it is no longer possible to use it due to the volume of its errors, but it is also impossible to refactor due to its monolithic organization.
And under even worseit is precisely this method that is understood with an interface full of setters, which are designed to initialize the object to its correct state. But here's the problem: what state will the object be in until all the necessary setters are called and its initialization is completed? In the wrong state, but the object will already be there and someone will definitely want to use it in this state. But someone else must know this very sequence (sequences?) of calling setters to complete the initialization of such an object. Isn't it a bit too much responsibility for the client code?
Let's imagine that we need to perform a flight on MI-8. But in order for the automated system of a modern airfield to give us permission to take off, we need to send it a special hash.
How could it be...
МИ-8 - старая штука, на ее борту механический бортовой компьютер - что-то по аналогии с машиной Ч. Беббиджа, только поменьше. Современные машины формируют этот хеш сами из своей телеметрии процесса запуска и маршрута полета. У нас же с собой только мобильник с ПО для формирования этого хеша вручную и обширная приборная панель МИ-8.
И вот, мы приступаем к запуску МИ-8: запускаем каждый механизм и вписываем показания приборов в ПО на мобильнике. Когда вертолет запущен и план полета установлен, мы жмем в ПО на мобильнике кнопку генерации хеша и получаем строчку этого хеша.
В связи с тем, что старой техники со своими условностями море, а формат хеша единый для всех, ПО ручной генерации хеша для каждой модели техники сделали отдельно от ПО передачи хеша в диспетчерскую аэродрома. Да и вообще, ПО генерации хеша для старой техники пишется в КБ производителя этой техники. Поэтому нам выдали строковое представление хеша и теперь его надо скопировать из одного ПО и вставить в нужное поле для отправки в другом ПО...
Так, стоп. Кажется хеш - это интерфейс, оператор ПО внезапно оказался посредником, а ПО для ручной генерации хеша - это билдер что ли? Все именно так.
Билдер хорошо знает устройство того типа, который он строит. Но ни о самом этом типе, ни о его устройстве посреднику можно не знать. Посредник может знать только интерфейс (это важно, т.е. даже не сам тип билдера, а только его интерфейс) билдера и интерфейс создаваемого объекта. Посредник имеет право обойтись малыми знаниями, которых достаточно для того чтобы передать артефакт работы билдера потребителю.
Билдер может строить объект совсем другого типа данных, отдает он всегда некоторый обобщенный интерфейс.
Вдобавок, создаваемый нашим билдером из примера хеш является DTO - т.е. Data Transfer Object, среди прочих свойств которого можно обозначить иммутабильность.

An immutable object can be obtained through the RAII or factory method idiom . But what if it takes a lot of parameters to create an immutable object? For example - 16 parameters. Or 23, as in one case . Or - 42 parameters.
What if some of these 42 parameters can be calculated from the rest, but sometimes they need to be specified explicitly?
What if there are 42 parameters in total, but only an arbitrary subset of these parameters needs to be used for construction? Reminds me of SQL queries, right?
RAII in this case chokes and becomes incomprehensible, and factory methods are required so much that it becomes difficult to manage them. The factory method idiom in this case begins to show its negative qualities and slow down development.
And the builder easily copes with all such cases. Let me remind you that, in principle, it is impossible to provide Immutable with setters, because this will break the immutability. And if you get out of the situation by converting one Immutable to another, then in this way you can only achieve a combinatorial explosion of immutable types, thereby burying further development.
But let's take another example. For example, the airfield control system receives a takeoff readiness signal from the side. Allow takeoff or not? It seems that first you need to check something before the resolution.
How could it be...
Нужно осмотреть журналы движения судов, сверить полетные расписания, сделать сверку документов пилота на допуск к полетам сегодня и еще что-нибудь малозначительное, вроде проверки расписания уборки взлетного полотна, чтобы какой-нибудь уборщик не взлетел вместе с бортом на воздух.
Все это - запросы к куче разных сервисов. Какой-то сервис может Быть слабонагруженным, а у какого-то могут быть перебои в работе. Система же распределенная и отказоустойчивая, в ней есть дублирующие узлы, на которые стоит отправить повтор запроса в случае отказа по запросу с основного узла.
Все данные нужно собрать в экземпляре сложной аналитической системы, которая состоит из множества довольно сложных стратегий работы со своими данными. Это все значит, что объект, с которым должна работать система диспетчеризации, будет иметь крайне сложный инвариант своего типа. Такой инвариант невозможно собрать на сеттерах, объект просто не склеится. К тому же, зачем системе диспетчеризации знать все тонкости типа такой сложной аналитической системы, когда ей нужен просто ответ - можно вот этому борту взлетать или нет?
Тут стоит отметить и то, что решение такой задачи в процедурном стиле приведет к все тому же комбинаторному взрыву и перегрузке по логике, потому что сервисный код запросов будет тесно переплетен с кодом логики принятия решений.
Но система диспетчеризации может просто взять билдер и сформировать на его базе граф асинхронных задач запросов к внешним системам. Именно в топологии этого графа и отражаются зависимости запросов к резервным системам, а результаты запросов складываются в билдер в произвольном, полностью асинхронном порядке.
Когда билдер возвращается к системе диспетчеризации из асинхронной среды, система диспетчеризации просто создает из него объект аналитической системы, с которой дальше и ведет все свои беседы по душам.

The builder is designed to solve the issues of type knowledge overload, fault tolerance overload and instantiation complexity overload. And it is worth using it for solving such problems.
Another builder allows you to separate the logic of setters separately from the logic of the resulting type, this can be very useful.
And, yes, it is also important to note that if the type invariant allows the presence of setters, if there is no problem that the user system knows this type in person, if the setters themselves do not overload the logic of the type, then the builder is clearly not needed.

M
Maxim Fedorov, 2020-11-03
@Maksclub

For example, the fact that the builder does not assemble itself, but another object / s based on the constructed details
. And the setters simply mutate the object, in fact, the structure is bare with public properties.
One pattern, another structure - that's the difference

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question