1

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 ???

Gaddour Mohamed
  • 119
  • 2
  • 9

1 Answers1

0

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:

enter image description here

If PizzaBuilder is a static member of Pizza: enter image description here

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.

Mr. Brickowski
  • 1,134
  • 1
  • 8
  • 20
  • Very good explanation and response. If i use Pizza constructor without builder a developper can not know about the existence of builder when he want to use Pizza object. When i put builder in constructor i will force the use of builder only to instantiate Pizza object. Can you also explain to me haw adding static builder method will broke the single responsability principle since it is not a new task implemented it is just a kind of new object constructor. And there is no risk. To change Pizza class when builder code change if we add builder interface as parameter – Gaddour Mohamed Apr 21 '22 at 00:14
  • @GaddourMohamed Good question, I updated my answer – Mr. Brickowski Apr 21 '22 at 04:46
  • Great answer I totally agreed with your response wich is perfect. I have just a comment about PrecisePizzaBuilder. if PizaBuilder and PrecisePizzaBuiler implements the same inerface PizzaBuilerInterface. then I use dependency injection of interface in getBuilder() method (looze coupling) . there is no need to change Pizza object when builder change. Single responsability will be respected. – Gaddour Mohamed Apr 21 '22 at 11:17
  • That is one way to do. However, using DI, I think we just shift the focus to other place to change. We may need to update some annotation, or xml. The change is still there and the reason for change is still there. But that could be a topic for another day. If you think this answer is helpful, you can help this community by accepting it. Sometime, someone may face the same challenge and they know what to follow. – Mr. Brickowski Apr 22 '22 at 06:25