Skip to content

Latest commit

 

History

History
158 lines (113 loc) · 7.15 KB

File metadata and controls

158 lines (113 loc) · 7.15 KB

Builder

Cenário de problema✈

Imagine que você está desenvolvendo um sistema de reserva de viagens. Neste sistema, um cliente pode reservar um pacote de viagem que inclui diferentes componentes como passagens aéreas, hospedagem, aluguel de carro, e passeios turísticos. Esses componentes têm muitas variações e opções (por exemplo, diferentes tipos de quartos, classes de voos, e categorias de carros). A construção de um objeto PacoteViagem (TravelPackage) com todos esses componentes pode se tornar complexa e propensa a erros, especialmente se você tiver que passar muitos parâmetros para o construtor ou criar várias subclasses para diferentes combinações de pacotes.


Utilização do padrão✅

O padrão Builder é ideal para este cenário porque ele separa a construção de um objeto complexo de sua representação, permitindo a criação passo a passo de objetos. Isso é útil quando um objeto pode ser configurado de várias maneiras, ou quando é necessário ocultar a complexidade da construção de um objeto do cliente.


Exemplo em Java(Fluent Interface Builder)☕

Aqui está um exemplo de implementação de Builder em Java para construir um objeto TravelPackage (Pacote de viagem):

//1
public class TravelPackage {
    private String flight;
    private String hotel;
    private String carRental;
    private String tour;

    //2
    private TravelPackage(Builder builder) {
        this.flight = builder.flight;
        this.hotel = builder.hotel;
        this.carRental = builder.carRental;
        this.tour = builder.tour;
    }

    @Override
    public String toString() {
        return "TravelPackage [Flight=" + flight + ", Hotel=" + hotel + 
               ", CarRental=" + carRental + ", Tour=" + tour + "]";
    }

    //3
    public static class Builder {
        private String flight;
        private String hotel;
        private String carRental;
        private String tour;

        public Builder() {
            //4
        }

        public Builder setFlight(String flight) {
            this.flight = flight;
            return this;
        }

        public Builder setHotel(String hotel) {
            this.hotel = hotel;
            return this;
        }

        public Builder setCarRental(String carRental) {
            this.carRental = carRental;
            return this;
        }

        public Builder setTour(String tour) {
            this.tour = tour;
            return this;
        }

        //5
        public TravelPackage build() {
            return new TravelPackage(this);
        }
    }
}
  1. Classe TravelPackage que será construída
  2. Construtor privado para forçar o uso do Builder
  3. Builder estático de TravelPackage
  4. Inicializa com valores padrão se necessário
  5. Método para construir o objeto final

Utilizando o Builder

Aqui está um exemplo de como utilizar o padrão Builder para criar diferentes pacotes de viagem:

public class TravelAgency {
    public static void main(String[] args) {
        //1
        TravelPackage basicPackage = new TravelPackage.Builder()
            .setFlight("Classe econômica😞")
            .setHotel("Hotel da antiga rodoviária🚌")
            .build();

        System.out.println(basicPackage);

        //2
        TravelPackage premiumPackage = new TravelPackage.Builder()
            .setFlight("Primeira classe papai💸")
            .setHotel("Hotel Copas Verdes🌴")
            .setCarRental("Honda Civic 2010 tunado")
            .setTour("Tour pelas maravilhas de Cascavel - PR")
            .build();

        System.out.println(premiumPackage);
    }
}
  1. Criação de um pacote básico de viagem
  2. Criação de um pacote de viagem personnalité

Explicação🤓

  • Classe TravelPackage: Esta é a classe que queremos construir usando o padrão Builder. Ela contém diversos atributos opcionais, como flight, hotel, carRental, e tour. O construtor é privado para garantir que apenas o Builder possa criar instâncias de TravelPackage.

  • Classe Builder: Esta classe estática interna é responsável por construir instâncias de TravelPackage. Ela fornece métodos para definir cada um dos atributos do pacote de viagem e um método build() que retorna a instância final de TravelPackage.

  • Fluent Builder: Cada método do Builder retorna a própria instância do Builder, permitindo a construção do objeto final de forma fluente (encadeada).


Quando utilizar?🤔

Builder é útil quando:

  • A criação de um objeto envolve muitas opções ou etapas complexas.
  • Você deseja evitar construtores telescópicos (construtores com um número excessivo de parâmetros).
  • Você deseja criar objetos imutáveis de forma segura e fácil.

Utilização mundo real🌎

  • Java StringBuilder: Ela permite construir strings de forma eficiente, especialmente quando você precisa concatenar várias partes. Em vez de criar múltiplas instâncias de String (que são imutáveis), você usa StringBuilder para construir a string passo a passo e, em seguida, obter o resultado final.
  • Java Streams API: Classe Stream permite criar pipelines de operações (como filter, map, collect) de forma fluida e imutável. O método collect é particularmente notável, pois pode usar diferentes tipos de Collector para construir o resultado final (como listas, conjuntos, mapas).
  • JavaFX: Utiliza para criar interfaces gráficas de usuário. Por exemplo, a classe FXMLLoader pode usar um FXML file (um tipo de configuração XML) para construir e configurar a interface gráfica.
  • Apache Camel: Apache Camel é um framework de integração que utiliza o padrão Builder para configurar rotas e pipelines de integração. As rotas podem ser configuradas usando um RouteBuilder, onde você define as regras e processos de integração passo a passo.

Esse padrão é frequentemente utilizado em situações onde um objeto pode ser configurado de diferentes maneiras, como na criação de objetos de configuração, documentos complexos, ou em sistemas onde é necessário ocultar a complexidade da construção de um objeto para simplificar o código cliente.

Builder com Director🎬

Poderíamos ainda evoluir nosso sistema e termos diversos pacotes para vários níveis, dos mais baratos aos mais caros. Nesse cenário, como sabemos o valores que devemos definir em todos esses casos, podemos evoluir nosso exemplo para a implementação utilizando Director, onde o diretor ficará responsável como irá executar as etapas de construção, enquanto o builder prove a implementação das etapas.

Ter uma classe diretor em seu programa não é estritamente necessário. Você pode chamar as etapas de construção em uma ordem específica diretamente, como em nosso exemplo anterior. Contudo, a classe diretor pode ser um bom lugar para colocar várias rotinas de construção para que você possa reutilizá-las em qualquer lugar do seu programa.

Podemos encontrar o mesmo exemplo da solução do sistema de reserva de viagens, porém utilizando Director em nosso repositório em /directorbuilder.

Para mais detalhes sobre esse modelo.