Builder Pattern em PHP
Tags: webdev, php, tutorial, programming
Published: 2022-09-08 10:52:34

O padrão Builder tem como objetivo simplificar a criação de objetos que possuem caracteristicas **parecidas**, mas valores diferentes em seus atributos e/ou diferentes implementações de métodos.

O Builder encapsula a criação do objeto, ocultando os detalhes de criação, delegando a responsabilidade para outras classes (mais especificas) implementarem a lógica. 

Com o padrão de projeto Builder separamos a **construção** do objeto de suas **representações**.

Situação problema - [Capítulo 5 do livro Design Patterns](https://www.casadocodigo.com.br/products/livro-design-paterns-php): 

_Atualmente, uma grande empresa que constrói nada menos do
que foguetes espaciais teve a necessidade de um software para a gestão dos seus produtos, e você foi contratado para executar este serviço. O sistema basicamente permitiria o cadastro dos foguetes que a empresa disponibiliza._

_Posteriormente, os foguetes previamente cadastrados passariam por diversas rotinas de teste através do mesmo sistema usado como catálogo para apresentação e venda._

_Cada foguete possui suas respectivas características: seu
modelo, um tipo de motor, tanque de combustível, número de
assentos para os tripulantes, entre uma infinidade de outras
características específicas de cada modelo._

_**Todos** os modelos dentro do sistema possuirão uma classe em comum, porém, **os atributos de cada um são diferentes** e devem ser informados quando criamos cada um dos objetos que os representa. Por exemplo, um foguete Modelo I possui 3 motores, já o foguete Modelo II possui 5; ou seja, são características particulares de cada um. Além disso, temos uma série de outros atributos que definem suas representações._

- **Product**: objeto complexo construido pelo Builder
- **Builder** (construtor): interface que serve de contrato das partes do Product 
- **Concrete Builder**: implementação concreta que constrói o Product 
- **Director**: classe responsável por definir a construção dos objetos utilizando a interface Builder

Começamos definindo nosso objeto complexo: 

```

namespace Builder;

// Objeto complexo que define um foguete
class FogueteProduct
{


    protected float $tanqueCombustivel;
    protected string $modelo;
    protected int $numeroMotores;
    protected int $numeroLugares;
    
    // getters e setters omitidos!!!

}
```
Em seguida, definimos nossa interface Builder através de uma classe abstrata: 

```

namespace Builder;

// esta é a interface que será extendida pelas outras classes
abstract class FogueteBuilder
{
    // sempre que uma classe implementar o Builder, teremos uma instancia de um foguete pronto para uso
    protected FogueteProduct $foguete;

    public function __construct()
    {
        $this->foguete = new FogueteProduct();
    }

    public function getFoguete():FogueteProduct
    {
        return $this->foguete;
    }

    // métodos que serão implementados por outros foguetes, especificando seus atributos variados que os diferem
    abstract public function buildTanqueCombustivel(): void;
    abstract public function buildModelo(): void;
    abstract public function buildMotores(): void;
    abstract public function buildNumeroLugares(): void;
}
```


```
class FogueteModeloIBuilder extends \Builder\FogueteBuilder
{

    // Repare que já possuimos acesso a um objeto do tipo foguete
    public function buildTanqueCombustivel(): void
    {
        $this->foguete->setTanqueCombustivel(1313);
    }

    public function buildModelo(): void
    {
        $this->foguete->setModelo('Foguete tipo 1');
    }

    public function buildMotores(): void
    {
        $this->foguete->setNumeroMotores(4);
    }

    public function buildNumeroLugares(): void
    {
        $this->foguete->setNumeroLugares(13);
    }
}
```

**Importante: Já possuímos uma instância de um foguete, assim como os métodos para criação do mesmo SEM CONHECERMOS verdadeiramente como essa criação é feita.** 

Isso não importa para as classes que implementarão os modelos, o montador (builder) do foguete não precisa conhecer a criação de cada parte do foguete, apenas implementar a especificação daquele objeto.

Se criarmos outro foguete, modelo 2: 

```
class FogueteModeloIIBuilder extends \Builder\FogueteBuilder
{

    // Repare que já possuimos acesso a um objeto do tipo foguete
    public function buildTanqueCombustivel(): void
    {
        $this->foguete->setTanqueCombustivel(850);
    }

    public function buildModelo(): void
    {
        $this->foguete->setModelo('Foguete tipo 2');
    }

    public function buildMotores(): void
    {
        $this->foguete->setNumeroMotores(54);
    }

    public function buildNumeroLugares(): void
    {
        $this->foguete->setNumeroLugares(133);
    }
}
```

Aqui a classe abstrata FogueteBuilder, serve como interface. Ela é a garantia para a classe FabricaFoguetesDirector de que todas as classes Builder conterão os métodos que precisamos para montar um foguete. 

Na propriedade $construtorDeFoguetes, uma instância de FogueteBuilder, corresponde aos nossos construtores concretos (Concrete Builder). 

```
use Builder\FogueteProduct;

class FoguetesDirectorFabrica
{
    protected \Builder\FogueteBuilder $construtorDeFoguetes;

    public function __construct(\Builder\FogueteBuilder $construtorDeFoguetes) {
        $this->construtorDeFoguetes = construtorDeFoguetes;
    }

    public function getFoguete(): FogueteProduct
    {
        return $this->construtorDeFoguetes->getFoguete();
    }
    public function construirFoguete(): void
    {
        $this->construtorDeFoguetes->buildModelo();
        $this->construtorDeFoguetes->buildMotores();
        $this->construtorDeFoguetes->buildTanqueCombustivel();
        $this->construtorDeFoguetes->buildNumeroLugares();
    }

}
```

Implementação no index: 

```
<?php 

use Builder\FogueteProduct;

// implementação
$montadoraDeFoguetesTestI = new Builder\FoguetesDirectorFabrica(new FogueteModeloIBuilder());
$montadoraDeFoguetesTestII = new Builder\FoguetesDirectorFabrica(new FogueteModeloIIBuilder());

print_r($montadoraDeFoguetesTestI);
print_r($montadoraDeFoguetesTestII);

$montadoraDeFoguetesTestI->construirFoguete();
$montadoraDeFoguetesTestI->getFoguete();
```

Repare como a utilização em si do objeto é simplificada, este é o principal objetivo do padrão Builder. 

Utilizando-o abstraimos a lógica de criação dos objetos, delegando para classes mais especificas implementarem as diferenças de propriedades e métodos (modelos 1 e 2), desta forma, o objeto final instanciado não precisa saber 'como' ele é feito, apenas que deve fazer.

Código do post: https://github.com/bbwithpointers/design-patterns-php