C#
- Padrão
Builder
![]() |
Neste artigo vou apresentar o padrão de projeto Builder. |
O objetivo do padrão Builder é separar a construção de um objeto complexo da sua representação de modo que o processo de construção possa criar diferentes representações.
Moral da História:
O padrão Builder é usado para separar o código que cria e o código que usa o
objeto.
Destacando que objetos complexos podem ser objetos com um construtor muito
grande com muitos parâmetros e objetos que possuem uma composição de vários
objetos
Dessa forma este padrão criacional pode produzir diferentes tipos e
representações de um objeto usando o mesmo código de construção, e, pode
construir um objeto complexo usando muitos objetos simples em uma abordagem
passo a passo.
Ele permite realizar o encadeamento de chamadas de métodos (method chaining).
Neste caso se você estiver usando uma programação fluente.
Este padrão de projeto é mais complexo que o Factory
Method e que o Abstract Factory.
Diagrama UML
A seguir temos o diagrama UML do padrão Builder segundo o Gof:
Product – A classe que representa o tipo de objeto complexo a ser construído pelo Builder; inclui classes que definem as partes constituintes, incluindo interfaces para montar as partes no resultado final;
Builder – Define uma interface/classe
abstrata para criar partes de um objeto Product ;
Define todos os passos que devem ser executados para criar um
Product;
ConcreteBuilder – Constrói e monta partes do
produto implementando a interface Builder, define e
acompanha a representação criada e fornece uma interface para retornar o
produto;
Director – Constrói um objeto usando a
interface Builder . (Controla o algoritmo que
gera o objeto do produto final)
Aqui temos um
relacionamento de agregação que é um tipo especial de associação onde as
informações de um objeto (chamado objeto-todo) precisam ser
complementados pelas informações contidas em um ou mais objetos de outra classe
(chamados objetos-parte); temos então o que conhecemos como
todo/parte.
Este relacionamento referido como relacionamento "tem um" , e, aqui temos que
todo o Director tem um
Builder.
Nota: O relacionamento de agregação
representa um vínculo fraco entre duas classes.
Vantagens e desvantagens do padrão Builder
As vantagens em usar o padrão são :
a- Permite
esconder os detalhes de como os objetos são criados;
b- Permite uma grande variedade de representações internas do objeto a ser
construído;
c- Fornece um grande controle sobre o processo de criação de objetos complexos;
d- Cada Builder é independente dos outros
builders e do restante da aplicação;
e- O Princípio da Responsabilidade Única (SRP) é
aplicado, uma vez que a construção complexa do objeto é isolada da lógica de
negócio deste objeto
E as desvantagens são :
a- O número de linhas de código aumenta conforme a complexidade do objeto e os
tipos de objetos que vamos construir;
b- Requer a criação de um ConcreteBuilder separado
para cada tipo diferente de produto;
Usos do padrão Builder
Podemos usar o padrão Builder:
- Quando a
instanciação de um objeto exige muitos parâmetros no construtor;
- Você deseja criar um conjunto de objetos relacionados ou dependentes que devem
ser usados juntos;
- O sistema precisar ser independente de como seus produtos são criados,
compostos ou representados;
- Quando você deseja esconder os detalhes do processo de construção do objeto
complexo;
Aplicando o padrão Adapter
Para mostrar um exemplo de implementação usando o padrão Builder veremos a montagem e a criação de Pizzas. Vamos criar uma aplicação Console usando o .NET 5.0 e a linguagem C#.
Primeiro veremos uma implementação para criar pizzas sem usar o padrão
Builder usando uma abordagem bem ingênua onde vamos
criar uma classe Pizza e nesta classe vamos definir os parâmetros que serão
usados para criar a pizza e faremos isso no construtor,
Assim vamos criar 3 enumerações para definir o tipo da
borda, tipo da massa e o tamanho e na classe Pizza
vamos criar 4 campos e dois métodos. Em uma pizza real a quantidade de
parâmetros com certeza seria maior e assim usar esta abordagem implicaria em ter
que informar diversos parâmetros no construtor o que não é uma boa pratica.
1- Enumerações
public enum Tamanho { Pequena = 1, Media = 2, Grande = 3 } |
public enum TipoBorda { Normal = 1, Recheada = 2 } |
public enum TipoMassa { Normal = 1, Fina = 2, Grossa = 3 } |
2- Classe Pizza
using System; using System.Collections.Generic;
namespace PizzariaSemBuilder public Pizza(TipoMassa tipoMassa, Tamanho tamanho,
public void PizzaConteudo() |
Esta implementação não usa o padrão Builder e na classe Program temos que criar uma instância da Pizza e passar os parâmetros que serão usados para criar a pizza:
using System; using System.Collections.Generic;
namespace PizzariaSemBuilder pizza1.PizzaConteudo(); |
Conforme a quantidade de parâmetros cresce fica mais difícil usar esta implementação.
Aplicando o padrão Builder para criar pizzas
Vejamos agora uma implementação onde vamos usar o padrão Builder para a criação de pizzas.
As enumerações permanecem as mesmas:
public enum Tamanho { Pequena = 1, Media = 2, Grande = 3 } |
public enum TipoBorda { Normal = 1, Recheada = 2 } |
public enum TipoMassa { Normal = 1, Fina = 2, Grossa = 3 } |
Vamos criar a classe Pizza que define o produto a ser criado:
using System; using System.Collections.Generic;
namespace Builder1.Product public void PizzaConteudo() |
A seguir vamos criar a classe PizzaBuilder que representa o Builder e define a interface abstrata para criar partes da pizza:
using Builder1.Product;
namespace Builder1.Builder
{
public abstract class PizzaBuilder
{
protected Pizza pizza;
public void CriaPizza()
{
pizza = new Pizza();
}
public Pizza GetPizza()
{
return pizza;
}
public abstract void PreparaMassa();
public abstract void IncluiIngredientes();
}
}
|
A seguir temos as classes concretas PizzaCalabreza e PizzaMussarela que implementam PizzaBuilder e criam as pizzas de calabreza e mussarela:
1- PizzaCalabreza
using Builder1.Builder; using Builder1.Product; using System.Collections.Generic;
namespace Builder1.ConcreteBuilder |
2- PizzaMussarela
using Builder1.Builder; using Builder1.Product; using System.Collections.Generic;
namespace Builder1.ConcreteBuilder |
A seguir vamos criar a classe Pizzaria que representa o Director e que constrói um objeto usando a interface Builder definindo a ordem de criação das partes :
using Builder1.Builder; using Builder1.Product; namespace Builder1.Director
//Constroi public Pizza GetPizza() |
E na classe Program estamos usando o Director para criar as pizzas por partes:
using Builder1.ConcreteBuilder; using Builder1.Director; using System; namespace Builder1 pizzaria = new Pizzaria(new PizzaMussarela()); Console.ReadKey(); |
Abaixo temos o resultado da execução do programa:
A seguir temos o diagrama de classes gerado pelo Visual Studio 2019 na
implementação:
O padrão
Builder é muitas vezes comparado com o padrão
Abstract Factory pois ambos podem ser utilizados
para a construção de objetos complexos.
A principal diferença entre eles é que o Builder
constrói objetos complexos passo a passo e procura evitar ser um anti-pattern,
enquanto o Abstract Factory constrói famílias de
objetos, simples ou mesmo complexos, de uma só vez.
Pegue o código
do projeto aqui :
Builder1.zip
"E Jesus clamou, e disse: Quem crê em mim, crê, não em
mim, mas naquele que me enviou.
E quem me vê a mim, vê aquele que me enviou."
João 12:44,45
Referências:
NET - Unit of Work - Padrão Unidade de ...
NET - O padrão de projeto Decorator
NET - Padrão de Projeto Builder
C# - O Padrão Strategy (revisitado)
NET - O padrão de projeto Command
NET - Apresentando o padrão Repository