Patrón Builder
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 93 de 104.
El patrón Builder separa la construcción de un objeto complejo de su representación, permitiéndote crear diferentes configuraciones paso a paso. Esto es especialmente útil cuando un objeto tiene muchos parámetros opcionales o requiere una secuencia de construcción específica.
En lugar de un constructor con muchos parámetros (lo cual resulta confuso), el Builder proporciona métodos claros y con nombre para cada opción de configuración:
#include <iostream>
#include <string>
class Pizza {
public:
std::string dough;
std::string sauce;
std::string topping;
bool cheese;
void describe() const {
std::cout << dough << " dough, " << sauce << " sauce, "
<< topping << (cheese ? ", with cheese" : "") << "\n";
}
};
class PizzaBuilder {
private:
Pizza pizza;
public:
PizzaBuilder& setDough(const std::string& d) {
pizza.dough = d;
return *this;
}
PizzaBuilder& setSauce(const std::string& s) {
pizza.sauce = s;
return *this;
}
PizzaBuilder& setTopping(const std::string& t) {
pizza.topping = t;
return *this;
}
PizzaBuilder& addCheese() {
pizza.cheese = true;
return *this;
}
Pizza build() { return pizza; }
};
int main() {
Pizza margherita = PizzaBuilder()
.setDough("thin")
.setSauce("tomato")
.setTopping("basil")
.addCheese()
.build();
margherita.describe();
}Cada método setter devuelve una referencia al builder (return *this), lo que permite el encadenamiento de métodos para una interfaz fluida. El método build() finaliza y devuelve el objeto construido.
Utilice Builder al construir objetos con muchos componentes opcionales, cuando desee un código de construcción legible o cuando el mismo proceso de construcción deba crear diferentes representaciones.
Desafío
FácilVamos a construir un sistema Computer Builder que permita a los usuarios configurar PCs personalizados paso a paso. Este es un escenario perfecto para el patrón Builder: las computadoras tienen muchos componentes opcionales y queremos una forma limpia y legible de ensamblar diferentes configuraciones.
Organizarás tu código en tres archivos:
Computer.h: Define la clase de producto que representa una computadora completamente configurada.Tu clase
Computerdebe almacenar estos componentes como miembros privados:cpu(string),ram(string),storage(string),gpu(string) yhasWifi(boolean). InicializahasWifienfalsey los strings como vacíos por defecto.Agrega un método público llamado
showSpecs()que muestre la configuración de la computadora. Para cada componente que haya sido establecido (string no vacío), imprímelo en su propia línea. La capacidad de wifi solo debe imprimirse si está habilitada.ComputerBuilder.h: Crea la clase builder que construye computadoras con una interfaz fluida.Tu clase
ComputerBuilderdebe tener un miembro privadoComputerque irá construyendo. Implementa estos métodos, cada uno devolviendo una referencia al builder para permitir el encadenamiento de métodos:setCPU(const std::string& cpu)setRAM(const std::string& ram)setStorage(const std::string& storage)setGPU(const std::string& gpu)addWifi()— habilita la capacidad de wifi
También implementa un método
build()que devuelva el objetoComputercompletado y reinicie el builder para una posible reutilización.main.cpp: Une todo para construir una computadora personalizada.Lee cuatro entradas:
- Modelo de CPU (string)
- Especificación de RAM (string)
- Especificación de almacenamiento (string)
- Si se debe incluir wifi:
yesono
Usa el
ComputerBuilderpara construir una computadora con la CPU, RAM y almacenamiento proporcionados. Si la entrada de wifi esyes, llama también aaddWifi(). Ten en cuenta que no se especifica ninguna GPU en esta configuración; el builder debe manejar los componentes opcionales con elegancia.Después de construir, llama a
showSpecs()en la computadora resultante para mostrar su configuración.
El método showSpecs() debe producir una salida en este formato (mostrando solo los componentes que fueron establecidos):
CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: EnabledPor ejemplo, con las entradas Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD y yes:
CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: EnabledCon las entradas AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD y no:
CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSDObserva cómo el patrón Builder hace que el proceso de construcción sea legible y flexible: puedes agregar o saltar componentes fácilmente, y el encadenamiento de métodos crea un código claro y autodocumentado. La línea de GPU no aparece porque nunca la configuramos, lo que demuestra cómo los builders manejan los componentes opcionales de forma natural.
Hoja de referencia
El patrón Builder separa la construcción de un objeto complejo de su representación, permitiendo una configuración paso a paso. Es útil cuando un objeto tiene muchos parámetros opcionales o requiere una secuencia de construcción específica.
En lugar de constructores con muchos parámetros, el Builder proporciona métodos claros y con nombre para cada opción de configuración:
class Pizza {
public:
std::string dough;
std::string sauce;
std::string topping;
bool cheese;
};
class PizzaBuilder {
private:
Pizza pizza;
public:
PizzaBuilder& setDough(const std::string& d) {
pizza.dough = d;
return *this; // Habilitar el encadenamiento de métodos
}
PizzaBuilder& setSauce(const std::string& s) {
pizza.sauce = s;
return *this;
}
PizzaBuilder& setTopping(const std::string& t) {
pizza.topping = t;
return *this;
}
PizzaBuilder& addCheese() {
pizza.cheese = true;
return *this;
}
Pizza build() { return pizza; }
};
// Uso con encadenamiento de métodos
Pizza margherita = PizzaBuilder()
.setDough("thin")
.setSauce("tomato")
.setTopping("basil")
.addCheese()
.build();Cada método setter devuelve return *this para habilitar el encadenamiento de métodos para una interfaz fluida. El método build() finaliza y devuelve el objeto construido.
Utilice Builder al construir objetos con muchos componentes opcionales, cuando desee un código de construcción legible o cuando el mismo proceso de construcción deba crear diferentes representaciones.
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "ComputerBuilder.h"
using namespace std;
int main() {
// Leer entradas
string cpu, ram, storage, wifiChoice;
getline(cin, cpu);
getline(cin, ram);
getline(cin, storage);
getline(cin, wifiChoice);
// TODO: Crear una instancia de ComputerBuilder
// TODO: Usar el encadenamiento de métodos para establecer CPU, RAM y storage
// TODO: Si wifiChoice es "yes", llamar también a addWifi()
// TODO: Llamar a build() para obtener el objeto Computer
// TODO: Llamar a showSpecs() en la computadora construida para mostrar la configuración
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados10Visión general de la STL
Visión general y filosofía de la STLContenedores de la STLIteradoresAlgoritmos de la STLFuntores y expresiones lambdaResumen - Frecuencia de palabras13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonFactory y Abstract FactoryPatrón BuilderPatrón ObserverPatrón Strategy2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico