Introducción: ¿por qué se necesitan los patrones de diseño en C++?
Los patrones de diseño (design patterns) son soluciones probadas para problemas típicos que surgen durante el desarrollo de software. En C++, con su potente sistema de tipos, RAII y herencia múltiple, los patrones adquieren una flexibilidad especial. Ayudan a que el código sea más legible, mantenible y escalable.
En este artículo, veremos 4 patrones clave del catálogo GoF (Gang of Four): Singleton, Factory Method, Observer y Strategy. Cada patrón se ilustrará con un ejemplo de código real en C++17/20.
Patrones creacionales: Singleton (Instancia única)
Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a ella. En C++, el enfoque moderno utiliza una variable estática local (thread-safe desde C++11).
Ejemplo: Logger de aplicación
class Logger {public: // Eliminamos copia y asignación Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete;
static Logger& getInstance() { static Logger instance; // thread-safe desde C++11 return instance; }
void log(const std::string& message) { std::lock_guard<std::mutex> lock(m_mutex); std::cout << "[LOG] " << message << std::endl; }
private: Logger() = default; // constructor privado ~Logger() = default; std::mutex m_mutex;};
// Usoint main() { Logger::getInstance().log("Aplicación iniciada"); Logger::getInstance().log("Error: archivo no encontrado"); return 0;}Ventajas: garantía de unicidad, inicialización perezosa, seguridad en hilos.
Desventajas: dificulta las pruebas (estado global), viola el principio de responsabilidad única.
Patrones creacionales: Factory Method (Método de fábrica)
Factory Method define una interfaz para crear un objeto, pero permite que las subclases decidan qué clase instanciar. En C++ a menudo se implementa mediante funciones virtuales y punteros inteligentes.
Ejemplo: Generación de diferentes tipos de documentos
// Producto abstractoclass Document {public: virtual void open() = 0; virtual void save() = 0; virtual ~Document() = default;};
// Productos concretosclass PDFDocument : public Document {public: void open() override { std::cout << "PDF abierto"; } void save() override { std::cout << "PDF guardado"; }};
class WordDocument : public Document {public: void open() override { std::cout << "Word abierto"; } void save() override { std::cout << "Word guardado"; }};
// Creador (Creator)class Application {public: virtual std::unique_ptr<Document> createDocument() = 0; virtual ~Application() = default;
void newDocument() { auto doc = createDocument(); doc->open(); // trabajo con el documento }};
// Creadores concretosclass PDFApplication : public Application {public: std::unique_ptr<Document> createDocument() override { return std::make_unique<PDFDocument>(); }};
class WordApplication : public Application {public: std::unique_ptr<Document> createDocument() override { return std::make_unique<WordDocument>(); }};
// Usoint main() { std::unique_ptr<Application> app = std::make_unique<PDFApplication>(); app->newDocument(); // Salida: "PDF abierto" return 0;}Este patrón es ideal cuando no se conocen de antemano los tipos exactos de los objetos a crear.
Patrones de comportamiento: Observer (Observador)
Blogs
Book Recommendations
Leer también
Gestión de memoria en Kotlin: cómo funciona el recolector de basura
Error JSX en TypeScript: la bandera --jsx y cómo solucionarlo
Principales errores de los desarrolladores PHP principiantes: cómo evitarlos
Algoritmos y estructuras de datos en Kotlin: guía práctica
Errores de novatos en Go: 5 errores que convierten el código en un infierno