I'm confused about using PHP Builder pattern in practise. In many documentation they propose using the Builder like this.
require 'Pizza.php';
require 'PizzaBuiler.php';
$piza_builder=(new PizzaBuilder('medium'))
->cheeze(true)
->bacon(true)
->build();
$pizza=new Pizza($piza_builder);
Pizza class use PizzaBuilder as constructor parameter and init class properties from it.
Why not instantiate object directly from Builder ??? is this bad (Anti-Pattern).
require 'Pizza.php';
require 'PizzaBuiler.php';
$piza= Pizza::getBuilder("medium")
->cheeze(true)
->bacon(true)
->build();
The only difference between two implemnettations is to modify build() function in Builder class to return new Pizza Object instead of of returning Builder instance.
can you advice me what clean builder to use ???
I think there are some variants for the builder pattern since it is a design pattern not a specification.
Builder pattern aims to solve the problem of complex object construction. As long as it does what it intends to do, it is ok.
Why not instantiate object directly from Builder?
For sure. Consider Pizza
is a conceptual representation of an Italian dish. PizzaBuilder
is a utility helps to build Pizza
. A Pizza
is good enough to exist on its own without a PizzaBuilder
. I think it is not a must to accept a Builder in Product constructor.
Moreover, I prefer not to include a static PizzaBuilder
in Pizza
because of the Single-responsibility principle. As stated above, a pizza is good enough to exist on its own. Pizza
has nothing to do with a PizzaBuilder
. It is PizzaBuilder
requires the knowledge of Pizza
to build Pizza
. I will leave any logic of Pizza
construction stay in the PizzaBuilder
and decouple it from Pizza
.
I would structure the code this way.
require 'Pizza.php';
require 'PizzaBuiler.php';
$pizza=(new PizzaBuilder('medium'))
->cheeze(true)
->bacon(true)
->build();
Update on 21-Apr-2022 to address comments
If i use Pizza constructor without builder a developer can not know about the existence of builder when he want to use Pizza object.
I think constructor is not a tool for advertising utilities built around that class. It is depends on how developer/user are knowledgeable about the framework, and base on that they program their application to achieve their purpose.
Can you also explain to me haw adding static builder method will broke the single responsibility principle
If PizzaBuilder
is standalone:
If PizzaBuilder
is a static member of Pizza
:
We have to think about what do PizzaBuilder
and getBuilder()
mean to Pizza
. Are they helping Pizza
to achieve something? If Pizza
is a conceptual representation of an Italian dish (of course this is only my interpretation. You may have your own since you know your application best), is getBuilder()
coherent to the rest of Pizza
construct?
In the future, if Pizza
need a change, what would be the possible reason? Maybe it has more ingredients to add. Maybe the PizzaBuilder
does not fit the business needs anymore. One day, if we need a new algorithm to precisely control the water and flour ratio. We develop a new PrecisePizzaBuilder
with a complex algorithm. Then we replace PrecisePizzaBuilder
with PizzaBuilder
. There would be two reasons to change Pizza
. It might be a hint that we couple two different things.
After all, I think there is no straight right or wrong. My answer, to some extent, involve my own comprehension to your application. I may provide bias opinion because I hate pizza (not really, I love it). I may understand your application wrong. Nevertheless, most important thing is that you give each module a purpose and preserve it throughout the entire application lifespan. Foresee which part will be changed in the future and what you can do today to make your future life easier. Then you will know what decision you need to make. This is OOP, SOLID principle, and design pattern all about. To make code flexible and maintainable.