Monte seu Type-Safe builder
Tags: java, codequality
Published: 2021-07-02 15:01:22

## Menu: programadores + fome = código sobre comida 

Dias atrás, após um dia de trabalho, estava estudando como de costume e me deu uma baita fome!

Resolvi terminar os estudos antes de ir jantar, mas um lanche do _Subway_ não saia da minha cabeça...

Então, quando menos percebi, a classe que eu tinha criado era assim:

```java

public class Lanche {

    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List<Vegetais> vegetais;
    private final Molho molho;

    public Lanche(Tamanho tamanho, Pao pao, Recheio recheio,
                  Queijo queijo, List<Vegetais> vegetais,
                  Molho molho) {
        this.tamanho = tamanho;
        this.pao = pao;
        this.recheio = recheio;
        this.queijo = queijo;
        this.vegetais = vegetais;
        this.molho = molho;
    }

}

```

Aparentemente, essa classe é muito simples: é Java puro, não tem nada demais... Mas, como todo código, sempre tem algo a ser melhorado. 

Vamos tentar olhar de forma crítica, decifrando o que podemos melhorar.

Não precisamos [_getters_ e _setters_](https://blog.caelum.com.br/nao-aprender-oo-getters-e-setters/). Partiremos da ideia que só criamos getters quando realmente for necessário. Como colocamos tudo o que precisamos no construtor, não temos o perigo de construir objetos inválidos.

Vamos ver como ficou a chamada para criar um lanche:
```java
new Lanche(Tamanho.GRANDE,Pao.PARMESAO_OREGANO,Recheio.CHURRASCO,
Queijo.PRATO, List.of(Vegetal.ALFACE, Vegetal.CEBOLA),
Molho.CHIPOTLE);
```

Bom, então o que tem para ser melhorado? No livro Código Limpo (_Clean Code_ de Robert Martin), no capítulo de funções há um item a respeito de parâmetros de funções (_Function Arguments_):

> A quantidade ideal de parâmetros para uma função é zero (nulo).
> Depois vem um (mônade), seguido de dois (díade).
> Sempre que possivel, devem-se evitar três parâmetros (tríade).
> Para mais de três, deve-se ter um motivo muito especial (políade) - mesmo assim não devem ser utilizados.

Pensando nessa frase, como faríamos para evitar chamar um construtor complexo ou vários _setters_ no objeto criado? 

## Entrada: o padrão Builder

Foi exatamente por esse motivo que surgiu o padrão _Builder_, descrito no livro Padrões de Projetos (_Design Patterns: Elements of Reusable Object-Oriented Software_ do GOF). O Builder é um padrão de projetos criacional que permite a separação da construção de um objeto complexo da sua representação.

O código ficaria assim:


```java

public class Lanche {
    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List<Vegetal> vegetais;
    private final Molho molho;

    private Lanche(LancheBuilder lancheBuilder) {
        this.tamanho = lancheBuilder.tamanho;
        this.pao = lancheBuilder.pao;
        this.recheio = lancheBuilder.recheio;
        this.queijo = lancheBuilder.queijo;
        this.vegetais = lancheBuilder.vegetais;
        this.molho = lancheBuilder.molho;
    }

    public static LancheBuilder umLanche() {
        return new LancheBuilder();
    }
 
    public static class LancheBuilder {
        private Tamanho tamanho;
        private Pao pao;
        private Recheio recheio;
        private Queijo queijo;
        private List<Vegetal> vegetais;
        private Molho molho;

        //Nao deixando chamar o construtor de fora desta classe
        private LancheBuilder() {
        }

        public LancheBuilder grande() {
            this.tamanho = Tamanho.GRANDE;
            return this;
        }

        public LancheBuilder normal() {
            this.tamanho = Tamanho.NORMAL;
            return this;
        }

        public LancheBuilder comPao(Pao pao) {
            this.pao = pao;
            return this;
        }

        public LancheBuilder comRecheio(Recheio recheio) {
            this.recheio = recheio;
            return this;
        }

        public LancheBuilder comQueijo(Queijo queijo) {
            this.queijo = queijo;
            return this;
        }

        public LancheBuilder comVegetais(Vegetal... vegetais) {
            this.vegetais = List.of(vegetais);
            return this;
        }

        public LancheBuilder comMolho(Molho molho) {
            this.molho = molho;
            return this;
        }

        public Lanche constroi() {
            return new Lanche(this);
        }
    }
}
```
E a chamada fica assim:
```java
Lanche.umLanche().grande()
                .comPao(PARMESAO_OREGANO)
                .comMolho(CHIPOTLE)
                .comQueijo(PRATO)
                .comRecheio(CHURRASCO)
                .comVegetais(ALFACE, CEBOLA)
                .constroi();

```

Veja que deixei o construtor da classe `Lanche` como privado e disponibilizei o método `umLanche`, que devolve um Builder. Isso está descrito no livro Java Efetivo (_Effective Java_ de Joshua Bloch) e é chamado de _static factory_. Diferente dos construtores conseguimos nomeá-los, fazendo com que fiquem mais descritivos. Além disso, podemos retornar um objeto que a princípio não é o mesmo de nossa classe.

Agora nosso problema foi resolvido: em nossa classe `Lanche` só temos um parâmetro no construtor: o `LancheBuilder`.

É o mais próximo do número de parâmetros ideal (zero/nulo).

Não sei se todos já foram a lanchonete chamada Subway, mas há regras para montar nosso lanche: 
![Subway funcionamento](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8pr3bfg69iwcwll9ftpy.png)

Costuma a funcionar um pouquinho diferente da foto, por isso vou detalhar a forma que os atendentes fazem:

1. Devemos escolher o recheio e o pão
1. Escolhemos um tamanho
1. Escolhemos o queijo
1. Escolhemos os vegetais
1. Por último, podemos ou não escolher um molho

Essa lanchonete funciona exatamente nessa ordem. Com isso, conseguimos identificar problemas em nossa solução atual: da forma que fizemos o Builder, não temos uma ordem de chamada, perceba que chamei tudo fora de ordem e veja o que conseguimos fazer: 
```java
Lanche.umLanche().constroi();

```

Nós pulamos todas as etapas, construimos um objeto em um estado inválido. OK, poderíamos lançar uma exceção caso algo esteja faltando.

Mas será que nós conseguiríamos pegar todos os erros em tempo de compilação? Aí, não precisaríamos esperar o projeto rodar para descobrir. O código nem seria compilado. Mas e agora? Como fazer para garantir que só vamos construir o objeto se passarmos por todas as etapas?

## Prato Principal: os Type-safe Builders

Os _Type-safe_ (ou Staged, ou Telescopic, ou Step) Builders nos auxiliam nas limitações que temos ao utilizar os Builders comuns.

A ideia é bem simples: aproveitar o sistema de tipos do Java para garantir que no momento de compilação todas as propriedades sejam definidas antes que a instância da classe seja construída.

Então, utilizando em nosso projeto a ideia do Type-safe Builder, construiremos nosso objeto em passos. Uma possível solução é a seguinte:

```java 
public class Lanche {
    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List<Vegetal> vegetais;
    private final Molho molho;

    private Lanche(LanchePassoFinalBuilder lanchePassoFinalBuilder) {
        this.tamanho = lanchePassoFinalBuilder.tamanho;
        this.pao = lanchePassoFinalBuilder.pao;
        this.recheio = lanchePassoFinalBuilder.recheio;
        this.queijo = lanchePassoFinalBuilder.queijo;
        this.vegetais = lanchePassoFinalBuilder.vegetais;
        this.molho = lanchePassoFinalBuilder.molho;
    }

    public static LancheBuilder umLanche(Recheio recheio, Pao pao) {
        return new LancheBuilder(recheio, pao);
    }

    static class LancheBuilder {
        private final Recheio recheio;
        private final Pao pao;

        private LancheBuilder(Recheio recheio, Pao pao) {
            this.recheio = recheio;
            this.pao = pao;
        }

        public LancheTerceiroPassoBuilder grande() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.GRANDE);
        }

        public LancheTerceiroPassoBuilder normal() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.NORMAL);
        }
    }

    static class LancheTerceiroPassoBuilder {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;

        public LancheTerceiroPassoBuilder(Recheio recheio,
                                          Pao pao,
                                          Tamanho tamanho) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
        }

        public LancheQuartoPassoBuilder comQueijo(Queijo queijo) {
            return new LancheQuartoPassoBuilder(recheio,
                    pao, tamanho, queijo);
        }
    }

    static class LancheQuartoPassoBuilder {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;
        private final Queijo queijo;

        public LancheQuartoPassoBuilder(Recheio recheio,
                                        Pao pao, Tamanho tamanho,
                                        Queijo queijo) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
            this.queijo = queijo;
        }

        public LanchePassoFinalBuilder comVegetais(Vegetal... vegetais) {
            return new LanchePassoFinalBuilder(tamanho,
                    recheio, pao,
                    queijo, List.of(vegetais));
        }
    }

    static class LanchePassoFinalBuilder {
        private final Tamanho tamanho;
        private final Recheio recheio;
        private final Pao pao;
        private final Queijo queijo;
        private final List<Vegetal> vegetais;
        private Molho molho;

        public LanchePassoFinalBuilder(Tamanho tamanho,
                                       Recheio recheio, Pao pao,
                                       Queijo queijo,
                                       List<Vegetal> vegetais) {
            this.tamanho = tamanho;
            this.recheio = recheio;
            this.pao = pao;
            this.queijo = queijo;
            this.vegetais = vegetais;
        }

        public LanchePassoFinalBuilder comMolho(Molho molho) {
            this.molho = molho;
            return this;
        }

        public Lanche build() {
            return new Lanche(this);
        }
    }
}
```

Dessa forma, não conseguimos pular nenhuma etapa obrigatória. Temos que seguir certinho as etapas para construção. E o melhor de tudo: quem for utilizar o Builder, não precisa conhecer a regra de negocio da lanchonete previamente. O código esta sucinto e claro o suficiente para que funcione como sua própria documentação.

Lembre-se: escrevemos códigos para que outras pessoas entendam.

O resultado da criação de um lanche com o Type-safe Builder ficou assim:

```java
Lanche.umLanche(CHURRASCO, TRES_QUEIJOS)
                .grande()
                .comQueijo(PRATO)
                .comVegetais(CEBOLA, TOMATE, ALFACE)
                .comMolho(CHIPOTLE)
                .build();
```

Os Type-safe Builders lidam muito bem com parâmetros opcionais. Basta trazê-los para perto da classe que permite a criação final do objeto.

Em Java, temos uma biblioteca chamada [jilt](https://github.com/skinny85/jilt), que pode nos auxiliar na criação de Type-safe Builders. Linguagens como o Kotlin também [ajudam na criação](https://kotlinlang.org/docs/type-safe-builders.html) desse tipo de Builder.

## Sobremesa: removendo indireções e finalizando com classe(*ou interfaces?*)

Podemos dizer que criamos uma [interface fluente](https://martinfowler.com/bliki/FluentInterface.html).

Fizemos o nosso design de código de uma forma que ao ler nós conseguimos entendê-lo como uma simples e bela frase de uma música.

Porém, vou te mostrar que ainda temos algumas indireções que podem gerar conflitos:

![Digitando . e aparecendo varias opções](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gaiyzdwzy122l0mvnmv3.png)

Viu só? Quando digitamos um ponto e nossa _IDE_ nos auxilia em qual método chamar, aparecem os passos internos.

Isso acontece porque definimos nossas classes internas de Lanche (os Builders dos passos) como pacote-privado (_package-private_).

Como será que podemos fazer para restringir ainda mais o acesso dessas classes internas?

Não podemos apenas definir como privado (_private_), pois assim seriam inacessíveis fora da classe `Lanche`.

Em Java, nós temos as interfaces. Assim como as classes (não internas), podem ser apenas pacote-privado ou públicas.

Com as interfaces, conseguimos garantir uma API, um contrato que é assinado independente de quem a implemente.

As interfaces de definição dos passos do nosso Type-safe Builder ficariam assim:

```java
public interface CriacaoLanchePassos {

    interface LancheBuilder {
        LancheTerceiroPasso grande();
        LancheTerceiroPasso normal();
    }

    interface LancheTerceiroPasso {
        LancheQuartoPasso comQueijo(Queijo queijo);
    }

    interface LancheQuartoPasso {
        LanchePassoFinal comVegetais(Vegetal... vegetais);
    }

    interface LanchePassoFinal {
        LanchePassoFinal comMolho(Molho molho);
        Lanche build();
    }
}
```


Nossas implementações dessa interfaces podem ser classes internas e privadas.

Bom, seguindo essas alterações, nosso código ficou:

```java
public class Lanche {
    private final Tamanho tamanho;
    private final Pao pao;
    private final Recheio recheio;
    private final Queijo queijo;
    private final List<Vegetal> vegetais;
    private final Molho molho;

    private Lanche(LanchePassoFinalBuilder lanchePassoFinalBuilder) {
        this.tamanho = lanchePassoFinalBuilder.tamanho;
        this.pao = lanchePassoFinalBuilder.pao;
        this.recheio = lanchePassoFinalBuilder.recheio;
        this.queijo = lanchePassoFinalBuilder.queijo;
        this.vegetais = lanchePassoFinalBuilder.vegetais;
        this.molho = lanchePassoFinalBuilder.molho;
    }

    public static CriacaoLanchePassos.LancheBuilder umLanche(Recheio recheio, Pao pao) {
        return new LancheBuilder(recheio, pao);
    }

    private static class LancheBuilder implements CriacaoLanchePassos.LancheBuilder {
        private final Recheio recheio;
        private final Pao pao;

        public LancheBuilder(Recheio recheio, Pao pao) {
            this.recheio = recheio;
            this.pao = pao;
        }

        public CriacaoLanchePassos.LancheTerceiroPasso grande() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.GRANDE);
        }

        public CriacaoLanchePassos.LancheTerceiroPasso normal() {
            return new LancheTerceiroPassoBuilder(recheio, pao,
                    Tamanho.NORMAL);
        }
    }

    private static class LancheTerceiroPassoBuilder implements CriacaoLanchePassos.LancheTerceiroPasso {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;

        private LancheTerceiroPassoBuilder(Recheio recheio,
                                          Pao pao,
                                          Tamanho tamanho) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
        }

        public CriacaoLanchePassos.LancheQuartoPasso comQueijo(Queijo queijo) {
            return new LancheQuartoPassoBuilder(recheio,
                    pao, tamanho, queijo);
        }
    }


    private static class LancheQuartoPassoBuilder implements CriacaoLanchePassos.LancheQuartoPasso {
        private final Recheio recheio;
        private final Pao pao;
        private final Tamanho tamanho;
        private final Queijo queijo;

        private LancheQuartoPassoBuilder(Recheio recheio,
                                        Pao pao, Tamanho tamanho,
                                        Queijo queijo) {
            this.recheio = recheio;
            this.pao = pao;
            this.tamanho = tamanho;
            this.queijo = queijo;
        }

        public CriacaoLanchePassos.LanchePassoFinal comVegetais(Vegetal... vegetais) {
            return new LanchePassoFinalBuilder(tamanho,
                    recheio, pao,
                    queijo, List.of(vegetais));
        }
    }

    private static class LanchePassoFinalBuilder implements CriacaoLanchePassos.LanchePassoFinal {
        private final Tamanho tamanho;
        private final Recheio recheio;
        private final Pao pao;
        private final Queijo queijo;
        private final List<Vegetal> vegetais;
        private Molho molho;

        private LanchePassoFinalBuilder(Tamanho tamanho,
                                       Recheio recheio, Pao pao,
                                       Queijo queijo,
                                       List<Vegetal> vegetais) {
            this.tamanho = tamanho;
            this.recheio = recheio;
            this.pao = pao;
            this.queijo = queijo;
            this.vegetais = vegetais;
        }

        public LanchePassoFinalBuilder comMolho(Molho molho) {
            this.molho = molho;
            return this;
        }

        public Lanche build() {
            return new Lanche(this);
        }
    }
}

```

Com isso, não temos mais acesso a detalhes internos de implementação:

![Digitando . e aparecendo apenas o método umLanche](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8ikl4ru2pwdjx2t57qh.png)
 

Conseguimos seguir estritamente a ideia de ocultação de informação (_information hiding_) ou encapsulamento. Não temos nenhuma indireção, temos apenas uma API fluente. Códigos assim são comumente encontrados em bibliotecas com um bom design de código.

Você pode encontrar o código desse post neste repositório: [github.com/gabrielronei/typesafebuilder-subway-exemplo](https://github.com/gabrielronei/typesafebuilder-subway-exemplo).

O repositório está divido em três branches, que são os três principais pontos que chegamos até aqui: `primeira_refatoracao`, `segunda_refatoracao` e `terceira_refatoracao`.


Existem várias soluções possiveis para esse código, porém essa foi uma solução que achei legal e quis mostrar para vocês. 

Espero que a ideia tenha ficado clara e que vocês consigam aproveitar algo desse tipo no dia-a-dia.

Lembrem-se, como tudo em Arquitetura/Design de Software, não é uma bala de prata. Sendo utilizada em excesso pode causar problemas e dificuldade de entendimento.

Mas é algo muito interessante que podemos ter como uma carta em nossa manga! :)

## Referências:
- Padrão Builder - https://bit.ly/3AiNfH0
- Artigo do criador do JILT - https://bit.ly/3x7YKPS

## Livros
- Código limpo (Clean code de Robert C. Martin) - https://amzn.to/3jzA8vh

- Java Efetivo (Effective Java de Joshua Bloch) - https://amzn.to/3yeSd67

- Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos(_Design Patterns: Elements of Reusable Object-Oriented Software_ de GOF) - https://amzn.to/3h99Sq9