phpdesign-patternsphp-7solid-principlesbuilder-pattern

HP OOP builder pattern use


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


Solution

  • 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.