tutoriales.com

Patrones de Diseño Creacionales en C++: Fábricas, Singletons y Builders al Descubierto

Este tutorial profundiza en los patrones de diseño creacionales más utilizados en C++: Factory Method, Abstract Factory, Singleton y Builder. Aprenderás a aplicarlos para construir objetos de forma flexible y eficiente, desacoplando la lógica de creación de la lógica de negocio. Optimiza la estructura de tu código y mejora su mantenibilidad.

Intermedio25 min de lectura6 views15 de marzo de 2026Reportar error

¡Bienvenido a este tutorial sobre Patrones de Diseño Creacionales en C++! 🚀

En el mundo del desarrollo de software, la creación de objetos es una tarea fundamental. Sin embargo, si esta creación no se gestiona de forma adecuada, puede llevar a un código acoplado, difícil de mantener y extender. Los patrones de diseño creacionales abordan precisamente este problema, proporcionando mecanismos para controlar y encapsular la forma en que los objetos son instanciados.

Este tutorial te guiará a través de los patrones creacionales más importantes en C++:

  • Factory Method
  • Abstract Factory
  • Singleton
  • Builder

Al final de este recorrido, tendrás una comprensión sólida de cuándo y cómo aplicar estos patrones para escribir código más robusto, flexible y mantenible.


¿Qué son los Patrones de Diseño Creacionales? 🤔

Los patrones de diseño creacionales son una categoría de patrones de diseño de software que se ocupan de los mecanismos de creación de objetos, intentando crear objetos de una manera que sea adecuada para la situación. Su objetivo principal es abstraer o encapsular la forma en que los objetos se instancian, haciéndolos más flexibles y desacoplados de los clientes que los utilizan.

Esto significa que la lógica para decidir qué tipo de objeto crear, o cómo configurarlo, no está mezclada directamente con el código que usa el objeto. En su lugar, esta lógica se centraliza en un lugar específico, permitiendo cambios futuros con un impacto mínimo.

💡 Consejo: Think of creational patterns as your personal object architects. They handle the complex blueprints and construction, so you can focus on furnishing and using the house (the object).

1. El Patrón Factory Method ✨

El patrón Factory Method (Método de Fábrica) es uno de los patrones creacionales más sencillos y ampliamente utilizados. Define una interfaz para crear un objeto, pero permite que las subclases decidan qué clase instanciar. En esencia, una clase delega la responsabilidad de la creación de objetos a sus subclases.

Propósito y Problema que Resuelve 🎯

Imagina que tienes una aplicación que trabaja con diferentes tipos de documentos (PDF, Word, Excel). En algún momento, necesitas abrir un documento, pero el tipo de documento puede variar en tiempo de ejecución. Si usaras un if-else o switch para instanciar cada tipo de documento, tu código se volvería rápidamente rígido y difícil de extender cada vez que añadieras un nuevo tipo de documento.

El Factory Method resuelve esto al proporcionar un "método de fábrica" que es responsable de la creación. Las subclases pueden sobrescribir este método para producir instancias de clases derivadas.

Estructura del Factory Method

El patrón Factory Method consta de los siguientes componentes:

  • Product (Producto): Declara la interfaz para los objetos que el método de fábrica crea.
  • ConcreteProduct (Producto Concreto): Implementa la interfaz Product.
  • Creator (Creador): Declara el método de fábrica, que devuelve un objeto del tipo Product. También puede definir una implementación por defecto del método de fábrica que devuelva un ConcreteProduct por defecto.
  • ConcreteCreator (Creador Concreto): Sobrescribe el método de fábrica para devolver una instancia de un ConcreteProduct específico.
«interface» Producto ProductoA ProductoB «abstract» Creador + factoryMethod() CreadorConcretoA + factoryMethod() CreadorConcretoB + factoryMethod() crea

Ejemplo Práctico: Fábrica de Vehículos 🚗

Vamos a crear un ejemplo simple con una fábrica de vehículos. Tendremos una interfaz Vehicle y clases concretas como Car y Motorcycle.

// 1. Product (Producto)
class Vehicle {
public:
    virtual void drive() const = 0;
    virtual ~Vehicle() = default;
};

// 2. ConcreteProduct (Producto Concreto)
class Car : public Vehicle {
public:
    void drive() const override {
        std::cout << "Driving a Car." << std::endl;
    }
};

class Motorcycle : public Vehicle {
public:
    void drive() const override {
        std::cout << "Driving a Motorcycle." << std::endl;
    }
};

// 3. Creator (Creador)
class VehicleCreator {
public:
    virtual Vehicle* createVehicle() const = 0;
    virtual ~VehicleCreator() = default;

    void someOperation() const {
        // Un creador puede tener lógica que usa el objeto creado
        Vehicle* vehicle = this->createVehicle();
        std::cout << "Creator: Launching some operations with the vehicle." << std::endl;
        vehicle->drive();
        delete vehicle; // Importante: liberar la memoria
    }
};

// 4. ConcreteCreator (Creador Concreto)
class CarCreator : public VehicleCreator {
public:
    Vehicle* createVehicle() const override {
        return new Car();
    }
};

class MotorcycleCreator : public VehicleCreator {
public:
    Vehicle* createVehicle() const override {
        return new Motorcycle();
    }
};

// Código cliente
void clientCode(const VehicleCreator& creator) {
    creator.someOperation();
}

int main() {
    std::cout << "App: Launched with the CarCreator." << std::endl;
    CarCreator carCreator;
    clientCode(carCreator);

    std::cout << "\nApp: Launched with the MotorcycleCreator." << std::endl;
    MotorcycleCreator motorcycleCreator;
    clientCode(motorcycleCreator);

    return 0;
}

Explicación:

  • Vehicle es la interfaz para todos los productos.
  • Car y Motorcycle son productos concretos.
  • VehicleCreator es el creador abstracto que declara createVehicle().
  • CarCreator y MotorcycleCreator son creadores concretos que implementan createVehicle() para crear sus respectivos productos.
  • El clientCode interactúa solo con la interfaz VehicleCreator, sin saber qué producto concreto se está creando, lo que demuestra el desacoplamiento.
Factory Method Completado

2. El Patrón Abstract Factory 🏭

El patrón Abstract Factory (Fábrica Abstracta) extiende la idea del Factory Method. Proporciona una interfaz para crear familias de objetos relacionados o dependientes sin especificar sus clases concretas. Es útil cuando su sistema debe ser independiente de cómo se crean, componen y representan sus productos.

Propósito y Problema que Resuelve 🎯

Considera una aplicación que soporta múltiples "temas" o "skins" (por ejemplo, Claro y Oscuro). Cada tema tiene varios componentes (botones, ventanas, cajas de texto) que deben ser consistentes con ese tema. Si tuvieras que crear manualmente los componentes para cada tema, el código sería repetitivo y propenso a errores. El Abstract Factory resuelve esto al permitirte crear "familias" completas de objetos.

Estructura del Abstract Factory

Los componentes clave son:

  • AbstractProduct (Producto Abstracto): Declara una interfaz para un tipo de objeto de producto (ej. Button, Window).
  • ConcreteProduct (Producto Concreto): Implementa la interfaz AbstractProduct (ej. LightButton, DarkButton).
  • AbstractFactory (Fábrica Abstracta): Declara una interfaz para operaciones que crean objetos AbstractProduct (ej. createButton(), createWindow()).
  • ConcreteFactory (Fábrica Concreta): Implementa las operaciones de la AbstractFactory para crear objetos ConcreteProduct que pertenecen a una familia específica (ej. LightThemeFactory crea LightButton, LightWindow).
  • Client (Cliente): Utiliza las interfaces AbstractFactory y AbstractProduct para interactuar con los productos.
Cliente <<interface>> AbstractFactory createProductA() createProductB() ConcreteFactory1 ConcreteFactory2 <<interface>> AbstractProductA ConcreteProductA1 ConcreteProductA2 <<interface>> AbstractProductB ConcreteProductB1 ConcreteProductB2

Ejemplo Práctico: Fábrica de Componentes UI por Tema 🖥️

Imaginemos una interfaz gráfica de usuario que puede tener un tema claro o uno oscuro, y cada tema tiene sus propios botones y casillas de verificación.

// 1. Abstract Products
class Button {
public:
    virtual void paint() const = 0;
    virtual ~Button() = default;
};

class Checkbox {
public:
    virtual void paint() const = 0;
    virtual ~Checkbox() = default;
};

// 2. Concrete Products (Light Theme)
class LightButton : public Button {
public:
    void paint() const override {
        std::cout << "Renderizando un botón del tema CLARO." << std::endl;
    }
};

class LightCheckbox : public Checkbox {
public:
    void paint() const override {
        std::cout << "Renderizando una casilla del tema CLARO." << std::endl;
    }
};

// 2. Concrete Products (Dark Theme)
class DarkButton : public Button {
public:
    void paint() const override {
        std::cout << "Renderizando un botón del tema OSCURO." << std::endl;
    }
};

class DarkCheckbox : public Checkbox {
public:
    void paint() const override {
        std::cout << "Renderizando una casilla del tema OSCURO." << std::endl;
    }
};

// 3. Abstract Factory
class GUIFactory {
public:
    virtual Button* createButton() const = 0;
    virtual Checkbox* createCheckbox() const = 0;
    virtual ~GUIFactory() = default;
};

// 4. Concrete Factories
class LightThemeFactory : public GUIFactory {
public:
    Button* createButton() const override {
        return new LightButton();
    }
    Checkbox* createCheckbox() const override {
        return new LightCheckbox();
    }
};

class DarkThemeFactory : public GUIFactory {
public:
    Button* createButton() const override {
        return new DarkButton();
    }
    Checkbox* createCheckbox() const override {
        return new DarkCheckbox();
    }
};

// 5. Client Code
void clientApp(const GUIFactory& factory) {
    Button* button = factory.createButton();
    Checkbox* checkbox = factory.createCheckbox();

    button->paint();
    checkbox->paint();

    delete button; // Liberar memoria
    delete checkbox;
}

int main() {
    std::cout << "Cliente: Usando el tema CLARO." << std::endl;
    LightThemeFactory lightFactory;
    clientApp(lightFactory);

    std::cout << "\nCliente: Usando el tema OSCURO." << std::endl;
    DarkThemeFactory darkFactory;
    clientApp(darkFactory);

    return 0;
}

Explicación:

  • Tenemos interfaces Button y Checkbox (Abstract Products).
  • Implementaciones concretas para Light y Dark (Concrete Products).
  • GUIFactory es la Abstract Factory con métodos para crear componentes.
  • LightThemeFactory y DarkThemeFactory son Concrete Factories que implementan GUIFactory para crear componentes del tema correspondiente.
  • El clientApp interactúa solo con la GUIFactory abstracta, sin conocer los detalles de las implementaciones concretas de los componentes.
Abstract Factory Completado

3. El Patrón Singleton 🥇

El patrón Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a ella. Es útil cuando se necesita que solo un objeto coordine acciones en todo el sistema, como un gestor de configuración, un pool de conexiones a bases de datos o un gestor de logging.

Propósito y Problema que Resuelve 🎯

Imagina que tienes una aplicación que necesita un objeto ConfigurationManager para leer los ajustes de un archivo. Si múltiples partes de tu aplicación crearan sus propias instancias de ConfigurationManager, podrían terminar leyendo el mismo archivo varias veces, o peor aún, leyendo diferentes versiones de los ajustes si el archivo se modifica en tiempo de ejecución. El Singleton asegura que solo haya una instancia, evitando inconsistencias y optimizando recursos.

⚠️ Advertencia: El Singleton a menudo se considera un anti-patrón si se abusa de él. Puede introducir acoplamiento global y dificultar las pruebas unitarias. Úsalo con cautela y solo cuando realmente necesites una única instancia global.

Estructura del Singleton

Para implementar un Singleton de forma segura en C++, especialmente en entornos concurrentes, se suelen seguir estos pasos:

  1. Constructor Privado: Evita que otras clases instancien directamente el Singleton.
  2. Método Estático de Acceso (getInstance()): Este método es el único punto de acceso para obtener la instancia única.
  3. Instancia Estática Privada: La instancia de la clase se mantiene como un miembro estático privado dentro de la propia clase.
  4. Deshabilitar Copia y Asignación: Para asegurar la unicidad, es crucial deshabilitar el constructor de copia y el operador de asignación.
Singleton - instance: Singleton* - Singleton() + getInstance(): Singleton* + Singleton(const Singleton&) = delete + operator=(const Singleton&) = delete Garantiza una única instancia y proporciona un punto de acceso global a ella.

Ejemplo Práctico: Gestor de Configuración ⚙️

Vamos a crear un ConfigurationManager que solo pueda tener una instancia.

#include <iostream>
#include <string>
#include <map>
#include <mutex> // Para seguridad en hilos

// El patrón Singleton
class ConfigurationManager {
private:
    // 1. Instancia estática privada del Singleton
    static ConfigurationManager* instance;
    // 2. Mutex para asegurar la inicialización segura en entornos multihilo
    static std::mutex mutex_;

    std::map<std::string, std::string> settings;

    // 3. Constructor privado para evitar instanciación externa
    ConfigurationManager() {
        std::cout << "ConfigurationManager: Inicializando (solo una vez)." << std::endl;
        // Simular carga de configuración desde un archivo
        settings["theme"] = "dark";
        settings["language"] = "es";
        settings["loglevel"] = "info";
    }

    // 4. Deshabilitar constructor de copia y operador de asignación
    ConfigurationManager(const ConfigurationManager&) = delete;
    ConfigurationManager& operator=(const ConfigurationManager&) = delete;

public:
    // 5. Método estático para obtener la única instancia
    static ConfigurationManager* getInstance() {
        std::lock_guard<std::mutex> lock(mutex_); // Bloquea para asegurar un acceso único a la instancia
        if (instance == nullptr) {
            instance = new ConfigurationManager();
        }
        return instance;
    }

    // Método para acceder a la configuración
    std::string getSetting(const std::string& key) const {
        auto it = settings.find(key);
        if (it != settings.end()) {
            return it->second;
        }
        return ""; // O lanzar una excepción
    }

    // Método para cambiar la configuración (si es mutable)
    void setSetting(const std::string& key, const std::string& value) {
        settings[key] = value;
        std::cout << "ConfigurationManager: Ajuste '" << key << "' actualizado a '" << value << "'." << std::endl;
    }

    // Un destructor estático o manual para limpiar la instancia
    static void destroyInstance() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
            std::cout << "ConfigurationManager: Instancia destruida." << std::endl;
        }
    }
};

// Inicialización de miembros estáticos
ConfigurationManager* ConfigurationManager::instance = nullptr;
std::mutex ConfigurationManager::mutex_;

// Código cliente
void clientComponent1() {
    ConfigurationManager* config = ConfigurationManager::getInstance();
    std::cout << "Componente 1: Tema -> " << config->getSetting("theme") << std::endl;
}

void clientComponent2() {
    ConfigurationManager* config = ConfigurationManager::getInstance();
    std::cout << "Componente 2: Idioma -> " << config->getSetting("language") << std::endl;
    config->setSetting("language", "en");
}

int main() {
    std::cout << "Iniciando la aplicación..." << std::endl;

    clientComponent1();
    clientComponent2();

    // Verificar si el cambio del Componente 2 afectó al Componente 1 (indirectamente)
    ConfigurationManager* finalConfig = ConfigurationManager::getInstance();
    std::cout << "Aplicación principal: Idioma final -> " << finalConfig->getSetting("language") << std::endl;

    // Importante: destruir la instancia para liberar memoria
    ConfigurationManager::destroyInstance();

    return 0;
}

Explicación:

  • El constructor de ConfigurationManager es privado, impidiendo la creación directa con new ConfigurationManager(). Esto fuerza el uso de getInstance().
  • getInstance() asegura que si la instance es nullptr, se crea por primera vez. Para entornos multihilo, std::mutex y std::lock_guard garantizan que solo un hilo pueda inicializarla, evitando condiciones de carrera.
  • Los constructores de copia y asignación están deleted para prevenir la creación de copias de la instancia única.
  • destroyInstance() es un método estático que permite la limpieza manual de la instancia Singleton, crucial para evitar fugas de memoria en aplicaciones de larga duración.
  • Ambos clientComponent1() y clientComponent2() obtienen la misma instancia, como se demuestra por el cambio de idioma persistente.
Singleton Completado

4. El Patrón Builder 👷

El patrón Builder (Constructor) se utiliza para construir objetos complejos paso a paso. Permite producir diferentes tipos y representaciones de un objeto utilizando el mismo proceso de construcción. Es especialmente útil cuando un objeto tiene muchas propiedades opcionales o configuraciones complejas.

Propósito y Problema que Resuelve 🎯

Considera la creación de un objeto Pizza. Una pizza puede tener una masa diferente (fina, gruesa), una salsa diferente (tomate, pesto), y muchas coberturas opcionales (queso, pepperoni, champiñones, etc.). Si usaras un constructor con muchos argumentos, sería difícil de leer, propenso a errores y poco flexible. O podrías tener muchos constructores sobrecargados, lo que también es un desastre.

El patrón Builder resuelve esto externalizando la construcción del objeto en un objeto Builder separado. El cliente configura el Builder con las propiedades deseadas y luego le pide al Builder que construya el objeto final.

🔥 Importante: El patrón Builder es excelente cuando la construcción de un objeto implica una secuencia compleja de pasos y cuando la misma lógica de construcción puede generar diferentes representaciones del objeto.

Estructura del Builder

Los participantes en el patrón Builder son:

  • Product (Producto): El objeto complejo que se está construyendo (ej. Pizza).
  • Builder (Constructor): Declara una interfaz abstracta para crear partes del objeto Product (ej. buildDough(), buildSauce(), buildToppings()).
  • ConcreteBuilder (Constructor Concreto): Implementa la interfaz Builder para construir y ensamblar las partes del Product. Mantiene un Product que construye y proporciona un método getResult() para obtener el producto terminado.
  • Director: Opcional. Construye un objeto utilizando la interfaz Builder. Puede definir el orden de los pasos de construcción.
Product + addPart(part) Director - builder: Builder + construct() «interface» Builder + buildPartA() + buildPartB() + getResult(): Product ConcreteBuilder - product: Product + buildPartA() + buildPartB() + getResult(): Product usa crea

Ejemplo Práctico: Constructor de Pizzas 🍕

Crearemos un Pizza y un PizzaBuilder para demostrar cómo se construye una pizza paso a paso.

#include <iostream>
#include <vector>
#include <string>

// 1. Product
class Pizza {
public:
    void setDough(const std::string& dough) { this->dough = dough; }
    void setSauce(const std::string& sauce) { this->sauce = sauce; }
    void addTopping(const std::string& topping) { toppings.push_back(topping); }
    void setCheese(const std::string& cheese) { this->cheese = cheese; }

    void display() const {
        std::cout << "--- Mi Pizza ---\n";
        std::cout << "Masa: " << dough << std::endl;
        std::cout << "Salsa: " << sauce << std::endl;
        std::cout << "Queso: " << cheese << std::endl;
        std::cout << "Toppings: ";
        for (const auto& t : toppings) {
            std::cout << t << " ";
        }
        std::cout << std::endl;
        std::cout << "----------------\n";
    }

private:
    std::string dough;
    std::string sauce;
    std::string cheese;
    std::vector<std::string> toppings;
};

// 2. Abstract Builder
class PizzaBuilder {
public:
    virtual void buildDough() = 0;
    virtual void buildSauce() = 0;
    virtual void buildCheese() = 0;
    virtual void buildToppings() = 0;
    virtual Pizza* getPizza() = 0;
    virtual ~PizzaBuilder() = default;
};

// 3. Concrete Builder
class MargheritaPizzaBuilder : public PizzaBuilder {
public:
    MargheritaPizzaBuilder() : pizza(new Pizza()) {}

    void buildDough() override { pizza->setDough("Masa Fina"); }
    void buildSauce() override { pizza->setSauce("Salsa de Tomate Clásica"); }
    void buildCheese() override { pizza->setCheese("Mozzarella Fresca"); }
    void buildToppings() override { /* Una Margherita clásica no lleva muchos toppings extra */ }

    Pizza* getPizza() override { return pizza; }

private:
    Pizza* pizza;
};

class CustomPizzaBuilder : public PizzaBuilder {
public:
    CustomPizzaBuilder() : pizza(new Pizza()) {}

    void buildDough() override { pizza->setDough("Masa Gruesa"); }
    void buildSauce() override { pizza->setSauce("Salsa Pesto"); }
    void buildCheese() override { pizza->setCheese("Mezcla de 4 Quesos"); }
    void buildToppings() override {
        pizza->addTopping("Pepperoni");
        pizza->addTopping("Champiñones");
        pizza->addTopping("Pimientos");
    }

    Pizza* getPizza() override { return pizza; }

private:
    Pizza* pizza;
};

// 4. Director (opcional pero útil para encapsular la secuencia de construcción)
class Cook {
public:
    void setPizzaBuilder(PizzaBuilder* builder) {
        this->builder = builder;
    }

    void constructPizza() {
        builder->buildDough();
        builder->buildSauce();
        builder->buildCheese();
        builder->buildToppings();
    }

private:
    PizzaBuilder* builder;
};

int main() {
    Cook cook;
    Pizza* pizza;

    std::cout << "Construyendo una Pizza Margherita..." << std::endl;
    MargheritaPizzaBuilder margheritaBuilder;
    cook.setPizzaBuilder(&margheritaBuilder);
    cook.constructPizza();
    pizza = margheritaBuilder.getPizza();
    pizza->display();
    delete pizza; // Liberar memoria

    std::cout << "\nConstruyendo una Pizza Personalizada..." << std::endl;
    CustomPizzaBuilder customBuilder;
    cook.setPizzaBuilder(&customBuilder);
    cook.constructPizza();
    pizza = customBuilder.getPizza();
    pizza->display();
    delete pizza; // Liberar memoria

    return 0;
}

Explicación:

  • Pizza es el producto final con sus propiedades.
  • PizzaBuilder es la interfaz abstracta para los constructores.
  • MargheritaPizzaBuilder y CustomPizzaBuilder son constructores concretos que implementan los pasos para crear tipos específicos de pizza.
  • El Cook actúa como Director, que sabe cómo usar un PizzaBuilder para construir una pizza en una secuencia estándar. El cliente puede interactuar directamente con el Builder o con el Director.
  • El main demuestra cómo diferentes constructores con el mismo Cook producen diferentes tipos de pizzas, utilizando un proceso de construcción paso a paso y fácil de leer.
Builder Completado

Comparativa y Elección de Patrones Creacionales 📊

Cada patrón creacional tiene su momento y lugar. Aquí hay una tabla comparativa para ayudarte a decidir cuál usar:

PatrónPropósito PrincipalCuándo UsarloBeneficiosDesventajas
Factory MethodDefine una interfaz para crear un objeto, pero deja que las subclases decidan qué clase instanciar.Cuando una clase no puede anticipar la clase de objetos que debe crear, o cuando una clase quiere que sus subclases especifiquen los objetos a crear.Desacopla la lógica de creación del cliente. Fácil de extender con nuevos productos.Puede requerir la creación de una subclase de creador para cada tipo de producto.
Abstract FactoryProporciona una interfaz para crear familias de objetos relacionados o dependientes sin especificar sus clases concretas.Cuando un sistema debe ser independiente de cómo se crean, componen y representan sus productos; o cuando un sistema debe configurarse con una de varias familias de productos.Garantiza la coherencia entre los productos de una familia. Aisla las clases concretas.Añadir nuevos tipos de producto es más complejo (requiere modificar la fábrica abstracta y todas las concretas).
SingletonGarantiza que una clase tenga solo una instancia y proporciona un punto de acceso global a ella.Cuando debe haber exactamente una instancia de una clase y debe ser accesible para los clientes desde un punto de acceso conocido.Acceso controlado a una única instancia. Evita duplicación de recursos.Puede introducir acoplamiento global. Dificulta las pruebas unitarias. Problemas en entornos concurrentes si no se implementa correctamente.
BuilderSepara la construcción de un objeto complejo de su representación, de modo que el mismo proceso de construcción pueda crear diferentes representaciones.Cuando la construcción de un objeto es compleja y tiene muchas partes opcionales, o cuando la misma lógica de construcción puede generar diferentes representaciones del objeto.Permite la construcción paso a paso de objetos complejos. Permite generar diferentes representaciones.Puede aumentar la complejidad del código con muchas clases de constructor.

Buenas Prácticas y Consideraciones Finales ✅

  • Usa std::unique_ptr o std::shared_ptr: En C++ moderno, es preferible usar smart pointers en lugar de punteros crudos para gestionar la memoria. Esto evitará muchas fugas de memoria y hará tu código más seguro. Los ejemplos en este tutorial usan new/delete para simplificar la explicación de los patrones, pero en un código real, usarías std::make_unique o std::make_shared.
    Ejemplo con `std::unique_ptr`
#include <memory>
// En lugar de:
// Vehicle* createVehicle() const override { return new Car(); }
// Usarías:
std::unique_ptr<Vehicle> createVehicle() const override { return std::make_unique<Car>(); }

// Y en el cliente, no necesitarías delete:
// std::unique_ptr<Vehicle> vehicle = this->createVehicle();
// vehicle->drive();
// delete vehicle; // ¡Ya no necesario!
</details>
  • Inyección de Dependencias: Los patrones creacionales pueden combinarse muy bien con la inyección de dependencias, especialmente el Factory Method y Abstract Factory, para permitir que el cliente no sepa ni siquiera qué fábrica concreta está usando, recibiendo la fábrica como una dependencia.

  • Claridad sobre Rendimiento: Generalmente, los patrones de diseño añaden una pequeña capa de abstracción que puede tener un impacto mínimo en el rendimiento. Sin embargo, los beneficios en mantenibilidad, flexibilidad y escalabilidad suelen superar con creces estas consideraciones, especialmente en sistemas complejos.

  • No te obsesiones: No intentes forzar un patrón de diseño donde no es necesario. Un código simple es a menudo el mejor código. Los patrones son herramientas para resolver problemas específicos, no metas a alcanzar en sí mismos.

📌 Nota: Mastering these patterns takes practice. Start by identifying common object creation problems in your existing code and see where a creational pattern could simplify or improve the structure.

Conclusión 🎉

Los patrones de diseño creacionales son herramientas poderosas en tu arsenal de C++ para construir software más robusto y flexible. Al dominar el Factory Method, Abstract Factory, Singleton y Builder, estarás mejor equipado para gestionar la complejidad de la creación de objetos, lo que conduce a un código más limpio, fácil de mantener y extensible.

Sigue practicando y experimentando con estos patrones. Con el tiempo, reconocerás intuitivamente las situaciones donde cada uno brilla, transformando tus diseños de software.

¡Feliz codificación! 🚀

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!