DEV Community

Cover image for Builder seguro e elegante
Leandro Lima
Leandro Lima

Posted on

2 1

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

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up