Separar a construção de um objeto da sua representação de modo que o mesmo processo de construção possa criar diferentes representações.
O padrão Builder é um design pattern do tipo Creational, porque ele busca solucionar problemas na criação de objetos (nesse caso com uma complexidade elevada), e foi proposto originalmente no livro Design Patterns: Elements of Reusable Object-Oriented Software.
Problema que ele se propõe a resolver
A aplicação do padrão Builder, é muito focada na construção de objetos complexos, que normalmente tendem a ter construtores infinitos dependendo da quantidade de opções que eles podem receber.
Por exemplo se pensarmos em uma classe que cuida da personalização de sanduíches no Subway, ele poderia ter as seguintes opções:
- Pão
- Carne
- Queijo
- Acompanhamentos
- Vegetais
- Temperos
- O lanche deve vir quente?
As opções tendem a ir ao infinito, e se pensarmos nisso em forma de código utilizando o formato comum de enviar esses dados via construtor, ficaria assim:
Agora se tivéssemos que instanciar um lanche vegetariano, sem queijo ou acompanhamentos, o processo se tornaria extremamente moroso, porque ao invés de definir apenas as informações que precisamos para o lanche, teríamos que definir todas as outras. E a instanciação do lanche acabaria assim:
Com esse pequeno exemplo, nosso construtor já ficou absurdamente longo, agora imagine se fossemos tendo que adicionar mais opções ao longo do tempo, conforme mais versões de lanches fossem surgindo, se tornaria cada vez mais complexo criar esses objetos (além de tornar a leitura e compreensão mais cansativa).
Como poderíamos resolver esse problema
Nesse ponto teremos o padrão builder entrando, a ideia central dele é que ao invés de precisar definir tudo através do construtor, teríamos uma classe chamada LancheDoSubwayBuilder
que seria responsável por controlar a construção desse objeto, adicionando as camadas conforme o usuário solicitasse.
Ou seja, ao invés de o usuário precisar avisar que ele não deseja carne no lanche, essa classe não solicitaria essa informação como obrigatória e apenas repassaria para a classe LancheDoSubway
algo relacionado a isso, quando o usuário chamasse o método responsável por isso.
Enormes chances que isso tenha ficado confuso agora, então vamos para um exemplo visual. Pensando no mesmo lanche que demonstramos acima, com uma classe builder a instanciação ficaria dessa forma:
Como vemos, apenas as informações necessárias foram passadas para a classe, através do encadeamento dos métodos necessários, sem obrigação do usuário ter contato por exemplo com a adição de Carne (que pra ele não é necessário, pois estamos fazendo um lanche vegetariano).
Para explicar como isso é possível, seria necessário vermos inicialmente como esse código seria escrito (aqui irei fazer uma versão simplificada, apenas a nível de exemplo):
Explicando esse código por partes, temos primeiro a variável privada _lancheDoSubway
que sempre vai ter a instância atual da classe LancheDoSubway
, vale aqui deixarmos claro que a classe Builder nunca busca substituir a classe principal, mas sim criar uma forma de tornar mais simplificada de instanciar diferentes versões dela (ex: um lanche vegetariano, um lanche de frango, um lanche com pimenta, etc).
💡A nível de curiosidade, como os exemplos são feitos em Typescript, adicionei o _ antes da variável privada, por ser uma convenção da linguagem e isso não tem nenhum efeito dentro do exemplo.
Depois disso temos o método reset
, que basicamente a ideia central dela é possibilitar que criemos lanches diferentes, sem precisar instanciar repetidamente a classe Builder. Como ela basicamente cria uma nova instância de LancheDoSubway
, evitamos que por exemplo um lanche vegetariano venha com Carne, porque nossa última criação de lanche não foi resetada.
Adicionar pão, vegetais, temperos e etc, não tem muito segredo. Basicamente estão realizando o trabalho que o construtor tinha, adicionando as camadas no lanche conforme formos solicitando.
Contudo, o pulo do gato tanto nesses métodos quanto nos outros, é o retorno que fazemos em cada um do this
. Eu sei que agora isso pode ficar extremamente confuso, mas vamos ir passo a passo.
Imaginemos que ao invés de retornar this
, o método adicionarPao
passasse a retornar a instância de pão criada dentro dele. Quando o método adicionarVegetais
fosse chamado, teríamos um erro, porque como esses métodos estão sendo chamados um após o outro, eles dependem do retorno que o método anterior disponibilizou para poderem continuar funcionando.
Ou seja, quando adicionarVegetais
recebesse o retorno de adicionarPao
, não seria mais a instância da classe LancheDoSubwayBuilder
e sim uma instância de pão, que muito provavelmente não existiria esse método. Por isso, é fundamental que sempre retornemos this
quando usando o padrão Builder.
Para finalizar, a classe diretora
E por último, gostaria de explicar um conceito extra relacionado a esse padrão, que é apenas uma opção quando queremos implementar ele.
Uma forma extra de aplicar o padrão Builder, envolve o uso de classes diretoras. Elas em suma, envolvem a ideia que ao invés de chamarmos cada passo (método) para construir o nosso lanche utilizando a classe LancheDoSubwayBuilder
(por exemplo), teríamos uma abstração maior acima disso.
Ao invés de deixarmos o usuário ciente de todos os passos possíveis na construção do nosso lanche, o que eles aceitam como parâmetros e etc. Com a classe diretora, escondemos isso e apenas fornecemos as opções que desejamos.
Então usando um exemplo em código, teríamos algo assim:
Nesse caso, o usuário não possuiria nenhuma noção se o método adicionarPao
dentro do builder aceita única e exclusivamente as opções tipo e tamanho do pão, pois é responsabilidade da classe Diretora chamar esse método e ela apenas expõe para o usuário as opções que acha necessária para formar o pão do lanche vegetariano.
Finalizando aqui, espero que esse artigo tenha te ajudado a entender melhor o padrão Builder. Tentei simplificar ao máximo a explicação, a fim de tornar mais fácil a compreensão do objetivo central desse padrão, mas vou deixar abaixo alguns materiais, caso você deseje se aprofundar no assunto.
Referências
- Refactoring Guru - Padrão Builder
- Otávio Miranda - Padrões de projeto
- Design Patterns: Elements of Reusable Object-Oriented Software
Foto de capa por Eaters Collective em Unsplash
Top comments (0)