DEV Community

Cover image for Builder seguro e elegante
Leandro Lima
Leandro Lima

Posted on

Builder seguro e elegante

Builder é um dos padrões de projeto da classe dos criacionais da bíblia da Gangue dos Quadro. Esse padrão permite construir objetos complexos passo a passo e um efeito colateral disso é uma construção fluente para os objetos.

var pessoa = Pessoa.builder().nome("João").sobrenome("das Couve").idade(36).build(); 
Enter fullscreen mode Exit fullscreen mode

Dessa forma, o que observamos do uso de builder e sua popularização está muito mais ligado à substituição de um construtor com muitos parâmetros por um design mais fluente do que propriamente o a intenção da GoF:

"...separação da construção de um objeto complexo da sua representação, de forma que o mesmo processo de construção possa criar diferentes representações."

Para diferenciar essa inteção do builder padrão, é comum chama-lo de Fluent Interface Builder.

O pattern está disponível nas IDEs para ser auto criado e ficou ainda mais conhecido no mundo Java através do Projeto Lombok que tirou um grande boilerplate e transformou em uma simples anotação.

Esse builder costuma ter o seguinte padrão:

package builder; public class Pessoa { private final String nome; private final String sobrenome; private final int idade; public Pessoa(String nome, String sobrenome, int idade) { this.nome = nome; this.sobrenome = sobrenome; this.idade = idade; } public static PessoaBuilder builder() { return new PessoaBuilder(); } // Getters and setters public static class PessoaBuilder { private String nome; private String sobrenome; private int idade; public PessoaBuilder nome(String nome) { this.nome = nome; return this; } public PessoaBuilder sobrenome(String sobrenome) { this.sobrenome = sobrenome; return this; } public PessoaBuilder idade(int idade) { this.idade = idade; return this; } public Pessoa build() { return new Pessoa(nome, sobrenome, idade); } } } 
Enter fullscreen mode Exit fullscreen mode

Mas sempre me incomodou que o builder, possuindo muitos passos, poderia contribuir para que o desenvolvedor desatento chame o build() prematuramente, isto é, com o objeto ainda incompleto. Isso só seria percebido em tempo de execução com uma exceção, ou pior, com a tentativa frustrada de acesso àquela propriedade esquecida.

Quando temos um construtor podemos impor que certos campos sejam final e isso obriga o desenvolvedor a preencher aqueles parâmetros, mas com o builder padrão, isso não é possível.

Esse incômodo, felizmente, acabou quando achei o artigo brilhante Simple Implementation of Fluent Builder - Safe Alternative To Traditional Builder do Sergiy Yevtushenko.

Esse gênio resolveu o problema e ainda de uma forma extremamente elegante!

package builder; public class Pessoa { private final String nome; private final String sobrenome; private final int idade; public Pessoa(String nome, String sobrenome, int idade) { this.nome = nome; this.sobrenome = sobrenome; this.idade = idade; } public static PessoaBuilder.Stage0 builder() { return nome -> sobrenome -> idade -> () -> new Pessoa(nome, sobrenome, idade); } private interface PessoaBuilder { interface Stage0 { Stage1 nome(String nome); } interface Stage1 { Stage2 sobrenome(String sobrenome); } interface Stage2 { Stage3 idade(int idade); } interface Stage3 { Pessoa build(); } } } 
Enter fullscreen mode Exit fullscreen mode

Aqui, definimos, de forma obrigatória, os parâmetros e a ordem em que eles devem ser preenchidos. Nosso builder é construído com passos representados por interfaces funcionais. Por conveniência, inseri esses passos dentro de uma interface que se comporta aqui como um namespace.

  1. O método builder retorna o primeiro estágio, uma classe que tem apena o método nome;
  2. Este retorna uma classe que tem também um único método que, sobrenome;
  3. Sobrenome retorna a Stage3 com o método build(),
  4. O build cria um objeto Pessoa.

Conclusão

O builder padrão é excelente quando se deseja dar ao desenvolvedor a flexibilidade de preencher os parâmetros que achar necessário, mantendo valores padrões para aqueles que não preencher. Para uma situação em que certos parâmetros não são opcionais, a solução apresentada é uma alternativa bastante enxuta e elegante.

Referências

Top comments (0)