Padrão Builder: Como construir objetos complexos em Java (Effective Java’s Builder pattern)

Classes com muitos atributos opcionais em seus métodos construtores podem trazer bastante sujeira para o código, além de aumentar a probabilidade de erros quanto mais atributos forem necessários para instanciar um objeto da classe em questão.

Considere o exemplo a seguir da classe Produto, que possui 8 atributos.

Digamos que um produto possa ser criado com apenas dois atributos, id e nome e, além disso, qualquer outra combinação possível de atributos. Nesse caso, precisamos de um construtor para atender esse requisito O código abaixo mostra a classe Produto com seu construtor.

O problema dessa abordagem é a verbosidade no código para criar produtos. Veja, por exemplo, como a criação de alguns produtos com diferentes atributos pode poluir facilmente o código. Além disso, essa abordagem é muito propícia a erros. Na hora de criar um produto você deverá redobrar a atenção, muitas vezes olhando a classe produto várias vezes, para não trocar a posição de atributos do mesmo tipo.

É preciso passar null para os atributos que não desejamos, tornando o código menos legível, mais trabalhoso e muito propenso a erros. Uma possível solução é fazer uma sobrecarga de métodos construtores, que recebem apenas os atributos que queremos. Nessa abordagem, a classe produto ficaria assim:

E a criação dos produtos ficaria assim:

Essa abordagem melhora bastante a facilidade de criar os objetos, mas ainda há um problema. E se quisermos também uma forma de criar produtos apenas com o id, o nome e a descrição. Parece que é só criar um construtor que receba um long e duas strings e o problema está resolvido, mas como já existe um método construtor com essa assinatura (o construtor que cria um produto básico com cor), não é possível adicionar um novo método construtor com essa especificação.

Isso é um problema. Mas é um problema já conhecido e resolvido por aí. Por isso a importância dos padrões de projeto. Para resolver isso, vamos utilizar o padrão Effective Java’s Builder, que nos ajuda a criar de forma segura e elegante classes com muitos atributos opcionais.

O padrão Effective Java’s Builder tem como base o conceito de interface fluente, onde os métodos podem ser encadeados e chamados como se estivesse escrevendo um texto. No caso do builder, a ideia é construir um objeto básico com os atributos obrigatórios e depois ir chamando outros métodos para os atributos opcionais de forma encadeada. Veja como fica a classe Produto com o padrão builder implementado.

A principal mudança foi a criação de uma classe interna chamada Builder para construir instâncias da classe Produto. O construtor da classe produto foi deixado com privado, dessa forma só é possível construir uma instância de produto por meio da classe interna Builder.

A classe Builder possui todos os atributos de produto e um construtor para um produto básico que recebe o id e o nome. Além disso, cada método para os demais atributos (descricao, valor, cor, etc) retorna o próprio builder, tornando possível as chamadas encadeadas. 

Para construir uma instância da classe Produto é necessário chamar o método build(), que chama o construtor privado da classe Produto passando o builder com os atributos de produto. O construtor da classe Produto apenas obtém os dados do builder.

Veja agora como fica a criação de um produto básico e de um produto com outros atributos.

A construção do objeto ficou muito mais simples, com uma escrita natural, além de tornar o código menos poluído e menos propenso a erros na construção dos objetos.

Além disso, o código fica mais flexível. É possível adicionar novas funcionalidades no builder, por exemplo um método validate() que, antes de construir o objeto, valida os dados e pode lançar uma exceção do tipo IllegalStateExeception caso haja alguma inconsistência nos dados.