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.
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.
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);
}
}
}
- Classe TravelPackage que será construída
- Construtor privado para forçar o uso do Builder
- Builder estático de TravelPackage
- Inicializa com valores padrão se necessário
- Método para construir o objeto final
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);
}
}
- Criação de um pacote básico de viagem
- Criação de um pacote de viagem personnalité
-
Classe
TravelPackage
: Esta é a classe que queremos construir usando o padrão Builder. Ela contém diversos atributos opcionais, comoflight
,hotel
,carRental
, etour
. O construtor é privado para garantir que apenas oBuilder
possa criar instâncias deTravelPackage
. -
Classe
Builder
: Esta classe estática interna é responsável por construir instâncias deTravelPackage
. Ela fornece métodos para definir cada um dos atributos do pacote de viagem e um métodobuild()
que retorna a instância final deTravelPackage
. -
Fluent Builder: Cada método do
Builder
retorna a própria instância doBuilder
, permitindo a construção do objeto final de forma fluente (encadeada).
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.
- 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.
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.