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)