Builder Classes Simplificado


O padrão Builder utiliza no mínimo 2 classes: 1 classe para instância a ser construída e 1 classe para a instância construtora.

Nesse artigo irei utilizar um Builder simplificado, que irá utilizar a mesma classe tanto para construir como para representar a instância final.

Unsplash image Photo by Christopher Burns on Unsplash

O padrão Builder já é muito conhecido e utilizado mundo afora.

Esse artigo não tem a pretensão de ensiná-lo para que serve esse padrão — há milhares de artigos sobre isso na Internet, com implementação em várias linguagens diferentes.

No entanto, um bom resumo é encontrado na Wikipedia:

“Separar a construção de um objeto complexo da sua representação de modo que o mesmo processo de construção possa criar diferentes representações.”

Como podemos ver nos exemplos em C# da Wikipedia, foi utilizado uma classe Car e uma interface ICarBuilder. Depois, temos uma implementação dessa interface com a definição da classe FerrariBuilder e, finalmente, outra classe para customizar a Ferrari chamada SportsCarBuildDirector.

Podemos simplificar esse exemplo?

Primeiramente, Car poderia ser uma interface ICar e não uma classe. Então, poderíamos ter a classe TFerrari que implementa ICar.

Na minha opinião, o único Builder do exemplo é a classe TSportsCarBuildDirector que customiza uma Ferrari para diretores. No entanto, eu também não concordo com sua existência e acho que podemos simplificar ainda mais esse exemplo — veja mais abaixo.

Vamos começar pela primeira interface, mantendo-a simples, retornando tipos primitivos:

ICar = interface
  function Model: string;
  function NumDoors: Integer;
  function Color: TColor;
  procedure Run;
end;      

Depois, codificamos a classe TFerrari, que implementa ICar. Veja que essa classe não é um Builder, mas sim apenas uma classe comum.

TFerrari = class(TInterfacedObject, ICar)
private
  fModel: string;
  fNumDoors: Integer;
  fColor: TColor;
public
  constructor Create(const aModel: string;
    aNumDoors: Integer; aColor: TColor);
  function Model: string;
  function NumDoors: Integer;
  function Color: TColor;
  procedure Run;
end;

Como disse acima, não acho necessidade de haver um Builder chamado TSportsCarBuildDirector. Para implementar essa classe seria necessário implementar uma nova interface ICarBuilder além da própria classe, o que seria desnecessário.

Podemos simplificar esse exemplo utilizando herança dessa forma:

TDirectorSportCar = class(TFerrari)
public
  constructor Create(aColor: TColor); reintroduce;
end;

constructor TDirectorSportCar.Create(aColor: TColor);
begin
  inherited Create('488 Spider', 2, aColor);
end;

Acabamos de criar um Builder mas utilizando a própria classe que irá representar um ICar, ou seja, a TDirectorSportCar. Não precisamos de mais uma interface abstrata como ICarBuilder ou sua implementação.

Para demonstrar o código, poderíamos ter algo assim:

procedure DoSomethingWithCars;
var
  car: ICar;
begin
  car := TDirectorSportCar.Create(clRed);
  car.Run;
end;

Sem Builders (aparentes), sem o método “Construct”, sem o método “GetResult”, e utilizando objetos imutáveis.

Até logo.

Posts Relacionados

  • Memória Segura Utilizando Instâncias de Interfaces

  • Classes Mutáveis vs Objetos Imutáveis

  • Implementando Interfaces Utilizando Diferente Assinaturas de Métodos

  • Usando Paths ao invés de Diretivas de Compilação

  • Trabalhando com Exceções em Requisições HTTP

  • Tipo object Continua Vivo

  • Array de Objetos

  • Variáveis Locais Deveriam ter Nomes Curtos

  • Como Dividir e Organizar o Código em Formulários com Muitos Widgets

  • Pascal Deveria ser Modernizado?