Design Patterns - Builder

Propósito

El Builder separa la construcción de un objeto complejo de su representación, de forma que el mismo proceso de construcción pueda crear diferentes representaciones.

Problema

La necesidad de crear un objeto complejo de manera flexible y controlada, evitando tener un constructor con una larga lista de parámetros, lo cual no es nada práctico.

Solución

La solución que propone el Builder es:

  • Encapsular la construcción del objeto: Separar la construcción de su representación. Esto permite que un mismo proceso de construcción cree diferentes representaciones del objeto.
  • Construcción paso a paso: El objeto se construye paso a paso bajo el control del “director“, que asegura una correcta construcción.

Estructura

builder

Participantes

  • Builder: Interfaz que declara los métodos para crear las partes de un objeto producto (Product).
  • ConcreteBuilder: Implementa la interfaz Builder para construir y ensamblar las partes del producto. Define la representación a crear y retorna el producto.
  • Director: Construye un objeto usando la interfaz Builder en una serie de pasos.
  • Product: Representa el objeto complejo a construir.

Cuándo Usarlo

Este patrón es recomendable cuando el proceso de construcción:

  • Debe ser independiente de las partes que componen el objeto.
  • Debe permitir diferentes representaciones del objeto que está siendo construido.

Ventajas

Mayor control sobre el proceso de construcción: Permite una construcción paso a paso, lo que da un control más fino.

Encapsulamiento y Claridad: Encapsula el código de construcción en un solo lugar sin “ensuciar” el resto del código.

Reutilización del Objeto de Construcción: El objeto constructor puede ser reutilizado para crear distintas variantes del objeto.

Desventajas

Complejidad Adicional: Introduce más clases y objetos (director, builder, builderconcreto), lo que puede complicar el código.
Requiere Crear un Constructor Separado: Para cada tipo diferente de objeto es necesario un constructor diferente.

Ejemplo: Sistema para armar computadoras personalizadas

Estamos construyendo un sistema para configurar computadoras personalizadas.

Las computadoras pueden tener diferentes componentes (CPU, GPU, memoria, etc.), y queremos permitir al cliente elegir estas partes.

Problema

Los clientes deben poder crear una computadora a partir de la configuración de cada una de sus partes.

Solución planteada

Implementamos un Builder que permite crear un objeto complejo (la computadora) a partir de la construcción de cada una de sus partes.

La interfaz ComputerBuilder define los pasos para ensamblar diferentes componentes de una computadora (CPU, GPU, memoria). GamingComputerBuilder es una implementación específica que ensambla una computadora gamer siguiendo esos pasos. Computer es la computadora ensamblada final, con los componentes seleccionados. ComputerShop actúa como el director, que guía el ensamblaje de la computadora mediante un ComputerBuilder.

builder

Código Java

Codificamos en Java lo que preparamos en el diagrama.

Definimos el Producto:


    class Computer {
      private String cpu;
      private String gpu;
      private String memory;
  
      public String getCpu() {
          return cpu;
      }
      public void setCpu(String cpu) {
          this.cpu = cpu;
      }
      public String getGpu() {
          return gpu;
      }
      public void setGpu(String gpu) {
          this.gpu = gpu;
      }
      public String getMemory() {
          return memory;
      }
      public void setMemory(String memory) {
          this.memory = memory;
      }
    }
  

Definimos la interfaz de construcción:


    interface ComputerBuilder {
      void buildCPU();
      void buildGPU();
      void buildMemory();
      Computer getComputer();
    }
  

Implementamos un constructor concreto para computadoras gamer:


    class GamingComputerBuilder implements ComputerBuilder {
      private Computer computer = new Computer();
  
      public void buildCPU() { 
          computer.setCPU(“High-end CPU”); 
      }
      
      public void buildGPU() { 
          computer.setGPU(“High-end GPU”); 
      }
      
      public void buildMemory() { 
          computer.setMemory(“64GB RAM”);
      }
  
      public Computer getComputer() { 
          return computer; 
      }
    }
  

Creamos el director, que utiliza el builder para construir la computadora:


    class ComputerShop {
      public Computer constructComputer(ComputerBuilder builder) {
          builder.buildCPU();
          builder.buildGPU();
          builder.buildMemory();
          return builder.getComputer();
      }
    }
  

Mapeo (del ejemplo a Participantes)

Los participantes que vimos antes son: Builder, ConcreteBuilder, Product, Director:

  • ComputerBuilder(Builder): Interfaz que declara los métodos para construir partes del producto (buildCPU, buildGPU, buildMemory) y para obtener el resultado (getComputer)
  • GamingComputerBuilder (ConcreteBuilder): Implementa la interfaz Builder para construir y ensamblar las partes del Product. Guarda el producto que está construyendo y devuelve el producto final
  • Computer (Product): el objeto complejo a construir. Contiene partes (cpu, gpu, memory) que el GamingComputerBuilder construirá y ensamblará.
  • ComputerShop (Director): define el orden en que se construirán las partes del product. Usa un Builder para construir la computadora en los pasos definidos.

Conclusiones

Este ejemplo pudimos ver el uso del patrón Builder ensamblando computadoras con distintas especificaciones, de manera estructurada y flexible.

Cada componente, como la CPU, GPU o memoria, se selecciona y se ensambla, paso a paso, facilitando la personalización para el cliente, sin complicar el proceso de construcción del objeto final.

Patrones relacionados

  • Factory Method

Las clases ConcreteBuilder suelen implementarse con métodos de fabricación (Factory Method). 

  • Abstract Factory

Ambos se parecen en que construyen objetos complejos. El Builder retorna el objeto complejo como paso final, mientras que el Abstract Factory lo devuelve de inmediato.

  • Composite

Muchas veces lo que construye el Builder es un Composite.