Introducción al Patrón Builder
1.1 Definición
El patrón Builder separa la construcción de un objeto complejo de su representación. Permite que el mismo proceso de construcción cree diferentes representaciones, descomponiendo el proceso en múltiples objetos simples y separando lo invariante de lo variable. Lo invariante son los componentes del objeto, mientras que lo variable es el contenido específico de cada componente.
Por ejemplo, en el proceso de construcción de un automóvil, los componentes como el chasis, el motor, las ruedas y el carrocería son invariantes, pero el proceso específico de construcción de cada componente puede variar.
1.2 Ventajas y Desventajas
a. Buena encapsulación, separación entre construcción y representación
b. Alta extensibilidad, cada constructor específico es independiente y los componentes están desacoplados
c. El llamador del objeto no necesita conocer el proceso de construcción específico, cumpliendo con el principio de Demeter
1.3 Roles del Patrón Builder
Producto: El objeto que se va a construir, típicamente un objeto complejo compuesto por múltiples componentes simples
Constructor Abstracto: Interfaz que contiene métodos abstractos para crear las subpartes del producto, e incluye un método para devolver el objeto complejo construido
Constructor Concreto: Implementa el constructor abstracto, completando la creación específica de las subpartes del producto complejo
Director: Utiliza los métodos de construcción y ensamblaje del constructor para crear el objeto complejo, sin preocuparse por la información específica del producto
Caso de Estudio del Patrón Builder
1. Contexto de Negocio
Construir un objeto automóvil que contiene componentes principales como el chasis, el motor, las ruedas y el carrocería.
2. Implementación en Código
a. Definición de clases de entidad del automóvil y sus componentes
1 /**
2 * Clase Automóvil
3 */
4 public class Automovil {
5
6 // Carrocería
7 private Carroceria carroceria;
8
9 // Motor
10 private Motor motor;
11
12 // Chasis
13 private Chasis chasis;
14
15 // Rueda
16 private Rueda rueda;
17
18 public Carroceria getCarroceria() {
19 return carroceria;
20 }
21
22 public void setCarroceria(Carroceria carroceria) {
23 this.carroceria = carroceria;
24 }
25
26 public Motor getMotor() {
27 return motor;
28 }
29
30 public void setMotor(Motor motor) {
31 this.motor = motor;
32 }
33
34 public Chasis getChasis() {
35 return chasis;
36 }
37
38 public void setChasis(Chasis chasis) {
39 this.chasis = chasis;
40 }
41
42 public Rueda getRueda() {
43 return rueda;
44 }
45
46 public void setRueda(Rueda rueda) {
47 this.rueda = rueda;
48 }
49 }
b. Definición del constructor abstracto, proporcionando un método para devolver el objeto y definiendo la construcción abstracta de las subpartes del automóvil
1 /**
2 * Constructor Abstracto de Automóvil
3 */
4 public abstract class ConstructorAutomovil {
5
6 protected Automovil automovil = new Automovil();
7
8 /** Devuelve el objeto automóvil construido*/
9 public Automovil construir(){
10 return automovil;
11 }
12
13 /** Construir carrocería*/
14 public abstract ConstructorAutomovil carroceria();
15
16 /** Construir motor*/
17 public abstract ConstructorAutomovil motor();
18
19 /** Construir chasis*/
20 public abstract ConstructorAutomovil chasis();
21
22 /** Construir ruedas*/
23 public abstract ConstructorAutomovil ruedas();
24 }
c. Implementación de constructores específicos, como el Constructor de Automóviles Mercedes
1 /**
2 * Implementación del Constructor de Automóviles Mercedes
3 */
4 public class ConstructorAutomovilMercedes extends ConstructorAutomovil{
5
6 @Override
7 public ConstructorAutomovil carroceria() {
8 System.out.println("Constructor Mercedes construyendo carrocería");
9 automovil.setCarroceria(new Carroceria("Carrocería Mercedes"));
10 return this;
11 }
12
13 @Override
14 public ConstructorAutomovil motor() {
15 System.out.println("Constructor Mercedes construyendo motor");
16 automovil.setMotor(new Motor("Motor Mercedes"));
17 return this;
18 }
19
20 @Override
21 public ConstructorAutomovil chasis() {
22 System.out.println("Constructor Mercedes construyendo chasis");
23 automovil.setChasis(new Chasis("Chasis Mercedes"));
24 return this;
25 }
26
27 @Override
28 public ConstructorAutomovil ruedas() {
29 System.out.println("Constructor Mercedes construyendo ruedas");
30 automovil.setRueda(new Rueda("Ruedas Mercedes"));
31 return this;
32 }
33 }
Código de prueba:
1 public static void main(String[] args){
2 ConstructorAutomovil constructor = new ConstructorAutomovilMercedes();
3 /** Construir objeto automóvil mediante el constructor Mercedes */
4 Automovil auto = constructor.carroceria().motor().chasis().construir();
5 System.out.println(JSON.toJSONString(auto));
6 }
1 Constructor Mercedes construyendo carrocería
2 Constructor Mercedes construyendo motor
3 Constructor Mercedes construyendo chasis
4 {"chasis":{"nombre":"Chasis Mercedes"},"motor":{"nombre":"Motor Mercedes"},"carroceria":{"nombre":"Carrocería Mercedes"}}
En la función main, el llamador no necesita preocuparse por cómo se construye el automóvil, solo necesita preocuparse por qué componentes se deben combinar en el objeto automóvil. Si el automóvil necesita agregar o eliminar algunos componentes, solo necesita agregar o eliminar el método correspondiente en el constructor abstracto. Por ejemplo, si se necesita agregar un componente de tablero, simplemente se define la clase Tablero, se agrega un atributo en Automóvil y se modifica el constructor abstracto para agregar el método correspondiente.
Definir la clase Tablero y modificar el constructor abstracto para agregar el método de construcción del tablero:
1 /**
2 * Constructor Abstracto de Automóvil
3 */
4 public abstract class ConstructorAutomovil {
5
6 protected Automovil automovil = new Automovil();
7
8 /** Devuelve el objeto automóvil construido*/
9 public Automovil construir(){
10 return automovil;
11 }
12
13 /** Construir carrocería*/
14 public abstract ConstructorAutomovil carroceria();
15
16 /** Construir motor*/
17 public abstract ConstructorAutomovil motor();
18
19 /** Construir chasis*/
20 public abstract ConstructorAutomovil chasis();
21
22 /** Construir ruedas*/
23 public abstract ConstructorAutomovil ruedas();
24
25 /** Construir tablero*/
26 public abstract ConstructorAutomovil tablero();
27 }
Al construir el automóvil, ahora se puede incluir la construcción del tablero:
1 public static void main(String[] args){
2 ConstructorAutomovil constructor = new ConstructorAutomovilMercedes();
3 /** Construir objeto automóvil mediante el constructor Mercedes */
4 Automovil auto = constructor.carroceria().motor().chasis().tablero().construir();
5 System.out.println(JSON.toJSONString(auto));
6 }
Además, en este ejemplo, los constructores específicos implementan directamente los detalles de consturcción. Se puede transformar para separar los detalles de construcción, asbtrayendo los componentes. Por ejemplo, si el requisito cambia y el automóvil ya no es construido por el constructor Mercedes, sino por un constructor de terceros que utiliza motor Porsche, carrocería Mercedes, chasis BMW y tablero Audi, entonces dejar los detalles de construcción al constructor específico no sería extensible. Por lo tanto, los constructores específicos deben separar la abstracción de los componentes de construcción. El código sería:
Abstraer los componentes:
/**
* Interfaz de Carrocería
*/
public interface Carroceria {
public String nombre();
}
/**
* Interfaz de Chasis
*/
public interface Chasis {
public String nombre();
}
/**
* Interfaz de Motor
*/
public interface Motor {
public String nombre();
}
/**
* Interfaz de Rueda
*/
public interface Rueda {
public String nombre();
}
/**
* Interfaz de Tablero
*/
public interface Tablero {
public String nombre();
}
Modificar la clase constructor abstracta, como los componentes son construidos externamente, se pueden eliminar las definiciones abstractas del constructor, y el constructor puede construir directamente:
1 /**
2 * Constructor de Automóvil
3 */
4 public class ConstructorAutomovil {
5
6 protected Automovil automovil = new Automovil();
7
8 /** Devuelve el objeto automóvil construido*/
9 public Automovil construir(){
10 return automovil;
11 }
12
13 /** Construir carrocería*/
14 public ConstructorAutomovil carroceria(Carroceria carroceria){
15 automovil.setCarroceria(carroceria);
16 return this;
17 }
18
19 /** Construir motor*/
20 public ConstructorAutomovil motor(Motor motor){
21 automovil.setMotor(motor);
22 return this;
23 }
24
25 /** Construir chasis*/
26 public ConstructorAutomovil chasis(Chasis chasis){
27 automovil.setChasis(chasis);
28 return this;
29 }
30
31 /** Construir ruedas*/
32 public ConstructorAutomovil ruedas(Rueda rueda){
33 automovil.setRueda(rueda);
34 return this;
35 }
36
37 /** Construir tablero*/
38 public ConstructorAutomovil tablero(Tablero tablero){
39 automovil.setTablero(tablero);
40 return this;
41 }
42 }
Código de prueba:
1 public static void main(String[] args){
2 ConstructorAutomovil constructor = new ConstructorAutomovil();
3 /** Construir objeto automóvil mediante el constructor */
4 Automovil auto = constructor
5 .carroceria(new CarroceriaMercedes())// Carrocería Mercedes
6 .motor(new MotorPorsche())// Motor Porsche
7 .chasis(new ChasisBMW())// Chasis BMW
8 .tablero(new TableroAudi())// Tablero Audi
9 .ruedas(new RuedasMichelin())// Ruedas Michelin
10 .construir();
11 System.out.println(auto.toString());
12 }
1 {"motor":"Motor Porsche","ruedas":"Ruedas Michelin","chasis":"Chasis BMW","tablero":"Tablero Audi","carroceria":"Carrocería Mercedes"}
Ambas implementaciones tienen sus propias ventajas, y se puede elegir la más conveniente y extensible según el contexto de negocio específico.
Comparación entre el Patrón Builder y el Patrón Factory
- El Builder no se enfoca en el proceso de producción de componentes, solo en el proceso de construcción de objetos complejos; mientras que el patrón Factory necesita preocuparse por el proceso de producción de cada componente;
- El Builder tiende a construir objetos con la misma estructura pero diferente contenido; el patrón Factory tiende a producir en masa objetos con la misma estructura y contenido idéntico;
- En el patrón Builder, el cliente puede controlar cómo se construyen los componentes del objeto; en el patrón Factory, el cliente no puede controlar los detalles de construcción del objeto;
Se puede decir que el patrón Builder se enfoca más en el proceso de creación del objeto; mientras que el patrón Factory se enfoca más en el resultado de la creación del objeto.