DEV Community

Bruno Barros
Bruno Barros

Posted on

Builder Pattern em PHP

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:

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

}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode
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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }

}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)