El patrón de diseño Builder en Java es un patron de diseño creacional. Permite separar la creación del objeto de su representación. En otras palabras, permite la creación del objeto paso a paso. Esto es especialmente util en objetos complejos.
¿Cuándo se debe usar el patrón Builder?
Imaginemos que tenemos un objeto con 8 atributos. Para crearlo necesitamos escribir un constructor con 8 argumentos. Pero ¿Qué pasa si no queremos usar los 8 atributos? ya que mandar null como parámetro es mala práctica ¿deberíamos crear un constructor para 7 y para cada combinación de parámetros existente? Para estos casos podemos aplicar el patron de diseño builder en Java.
¿Cuáles son las ventajas de usar el patrón Builder?
- Flexibilidad: Permite la creación de objetos con el número de parametron deseado de manera natural.
- Facil lectura: La implementación de dicho patron es fácil de seguir y entender.
- Inmutabilidad: Es posible forzar la inmutabilidad una vez que el objeto a terminado de ser construido. Garantizando así la seguridad del hilo de posibles modificaciones no deseadas
¿Cómo se implementa el patrón Builder en Java?
Ya que los patrones de diseño con soluciones abstractas a proyectos recurrentes podemos adaptar nuestras implementaciones según el contexto en el que nos encontramos. Por esta razón aquí escribiremos 3 ejemplos de cómo implementar el patron de diseño builder en java
Patrón de diseño Builder con Java clasico
Esta es la manera clásica de crear el patron de diseño Builder en java. Lo primero que debemos notar son los atributos private final de nuestro objeto los métodos getter y la omisión de los métodos setter. Lo que hace inmutables los valores del mismo. Después podemos ver clase estática Builder que repite los mismos campos de la clase principal Product y cada atributo tiene un método para establecer información. Podemos ver el constructor que solo recibe el objeto builder. Y por último podemos el ver el método build que regrese un objeto de tipo Product
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | package com.funcionaenmimaquina.builder.classic; public class Product {     private final String name;     private final String description;     private final double price;     private final String category;     private final String imageUrl;     private final String brand;      private final String sku;     private Product(Builder builder) {         this.name = builder.name;         this.description = builder.description;         this.price = builder.price;         this.category = builder.category;         this.imageUrl = builder.imageUrl;         this.brand = builder.brand;          this.sku = builder.sku;     }     public static class Builder{         private String name;         private String description;         private double price;         private String category;         private String imageUrl;         private String brand;          private String sku;         public Builder name(String name) {             this.name = name;             return this;         }         public Builder description(String description) {             this.description = description;             return this;         }         public Builder price(double price) {             this.price = price;             return this;         }         public Builder category(String category) {             this.category = category;             return this;         }         public Builder imageUrl(String imageUrl) {             this.imageUrl = imageUrl;             return this;         }         public Builder brand(String brand) {             this.brand = brand;              return this;         }          public Builder sku(String sku) {             this.sku = sku;              return this;         }          public Product build() {             return new Product(this);         }     }     @Override     public String toString() {         return "Product{" +                 "name='" + name + '\'' +                 ", description='" + description + '\'' +                 ", price=" + price +                 ", category='" + category + '\'' +                 ", imageUrl='" + imageUrl + '\'' +                 ", brand='" + brand + '\'' +                 ", sku='" + sku + '\'' +                 '}';     }     public String getName() {         return name;     }     public String getDescription() {         return description;     }     public double getPrice() {         return price;     }     public String getCategory() {         return category;     }     public String getImageUrl() {         return imageUrl;     }     public String getBrand() {         return brand;     }     public String getSku() {         return sku;     } } | 
Ahora podemos ocupar el builder en Java de la siguiente forma. Creamos un objeto de tipo ProductBuilder lo que nos permite acceder a los métodos de asignación de valores de los atributos y al final llamamos al método Build para terminar la construcción de nuestro objeto.
| 1 2 3 4 5 6 7 8 9 10 11 12 | static void runClassicBuilder() {         System.out.println("Classic Builder Pattern Example");         Product product = new Product.Builder().name("Laptop")                 .description("High performance laptop")                 .price(1200.00)                 .category("Electronics")                 .imageUrl("http://example.com/laptop.jpg")                 .brand("BrandX")                 .sku("SKU12345").build();         System.out.println(product.toString());     } | 
Patron de diseño Builder Genérico con lambdas
Gracias a las lambdas podemos crear un builder genérico para poder usarlo con todos nuestros Objetos.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Builder<T> {  private final Supplier<T> supplier;     private Builder(Supplier<T> supplier) {         this.supplier = supplier;     }     public static <T> Builder<T> of(Supplier<T> supplier) {         return new Builder<>(supplier);     }     public <P> Builder<T> with(BiConsumer<T, P> consumer, P value) {         return new Builder<>(() -> {             T object = supplier.get();             consumer.accept(object, value);             return object;         });     }     public T build() {         return supplier.get();     } } | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class User {     private String name;     private String lastname;     private String secondLastName;     private String phone;     private String email;     // getters and setters     @Override     public String toString() {         return "User{" +                 "name='" + name + '\'' +                 ", lastname='" + lastname + '\'' +                 ", secondLastName='" + secondLastName + '\'' +                 ", phone='" + phone + '\'' +                 ", email='" + email + '\'' +                 '}';     } } | 
Este builder generico usa el metodo of() para asignar el tipo de dato y el metodo with para asignar el valor al atributo del objeto. El uso de este builder se ve elegante y amigable la lectura. Ademas de que brinda flexibilidad pero al basarse en los setters del objeto deja a un lado de la inmutabilidad.
| 1 2 3 4 5 6 7 8 9 10 11 | static void runGenericBuilder() {         System.out.println("Generic Builder Pattern Example");         User user = Builder.of(User::new)             .with(User::setName, "John")             .with(User::setLastname, "Doeh")             .with(User::setSecondLastName, "Doeh")             .with(User::setPhone, "555555")             .with(User::setEmail, "mail@mail.com")             .build();         System.out.println(user.toString()); } | 
Patron de Diseño Builder en Java con Lombok
Lombok es una libreria en Java que nos ayuda a eliminitar el codigo repetitivo mediante anotaciones. Entre sus muchas funciones nos da una manera de implementar el patron de diseño Builder de manera simple.
| 1 2 3 4 5 6 7 8 9 | @Builder @Getter @ToString public class Videogame {     private String name;     private String platform;     private String category; } | 
| 1 2 3 4 5 6 7 8 9 |  static void runLombokBuilder() {         System.out.println("Lombok Builder Pattern Example");           Videogame videogame = Videogame.builder().name("The Legend of Zelda")                 .platform("Nintendo Switch")                 .category("Action-Adventure")                 .build();         System.out.println(videogame.toString());     } | 
Tambien podemos usar la anotacion @Builder en los Records.
| 1 2 3 4 | @Builder public record SoccerTeam(String name, String country, String couch) { } | 
| 1 2 3 4 5 6 7 8 9 10 | static void runRecordBuilder() {         System.out.println("Record Builder Pattern Example");         Smartphone smartphone = Smartphone.builder()                 .model("iPhone 14")                 .brand("Apple")                 .operatingSystem("iOS")                 .price(999.99)                 .build();         System.out.println(smartphone.toString());     } | 
Patron de diseño Builder usando Records en Java
Los records nos llegaron a Java con la misíon de crear clases inmutables sin código boilerplate. Si queremos usar el patron de diseño Builder en Java podemos hacerlo de la siguiente forma:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public record Smartphone(String model, String brand, String operatingSystem,  double price) {   public static Builder builder() {     return new Builder();   }   public static class Builder {     private String model;     private String brand;     private String operatingSystem;     private double price;     public Builder model(String model) {       this.model = model;       return this;     }     public Builder brand(String brand) {       this.brand = brand;       return this;     }     public Builder operatingSystem(String operatingSystem) {       this.operatingSystem = operatingSystem;       return this;     }     public Builder price(double price) {       this.price = price;       return this;     }     public Smartphone build() {       return new Smartphone(model, brand, operatingSystem, price);     }   } } | 
En el codigo anterior podemos observar el metodo build que crear el objeto y metodo estatico build que regresa el objeto Builder para la poder acceder para a los metodos de asignación de valores.
| 1 2 3 4 5 6 7 8 9 10 | static void runRecordBuilder() {         System.out.println("Record Builder Pattern Example");         Smartphone smartphone = Smartphone.builder()                 .model("iPhone 14")                 .brand("Apple")                 .operatingSystem("iOS")                 .price(999.99)                 .build();         System.out.println(smartphone.toString()); } | 
Demostración
Conclusión
Ahora conocemos para que sirve el patron de diseño Builder y vimos que existenvarias maneras de implementarlo. Es importante conocer el contexto de nuestor proyecto para saber cual es la que mas se adapta a nuestras necesidades.
 
					 
											 
											