Patrones de diseño avanzados en C++: de la teoría al código real
Los patrones de diseño son soluciones probadas para problemas típicos en la programación orientada a objetos. Si ya dominas los patrones básicos (Singleton, Factory, Observer), es hora de pasar al siguiente nivel. En este artículo analizaremos cuatro patrones avanzados: Visitor, Strategy, State y Command. Todos los ejemplos están escritos en C++17/20 moderno usando punteros inteligentes, polimorfismo y plantillas.
1. Patrón Visitor (Visitante)
El patrón Visitor permite agregar nuevas operaciones a una jerarquía de clases sin modificar las clases mismas. Es una solución ideal para trabajar con árboles AST, escenas gráficas o sistemas de exportación.
Problema
Tienes una jerarquía de figuras geométricas (Circle, Rectangle, Triangle). Necesitas implementar operaciones: cálculo de área, dibujo, serialización a JSON. Si agregas métodos en cada clase, se viola el principio de abierto/cerrado.
Solución en C++17
#include <iostream>#include <memory>#include <vector>
// Forward declarationclass Circle;class Rectangle;
class Visitor {public: virtual void visit(Circle& c) = 0; virtual void visit(Rectangle& r) = 0; virtual ~Visitor() = default;};
class Shape {public: virtual void accept(Visitor& v) = 0; virtual ~Shape() = default;};
class Circle : public Shape {public: double radius = 5.0; void accept(Visitor& v) override { v.visit(*this); }};
class Rectangle : public Shape {public: double width = 4.0, height = 6.0; void accept(Visitor& v) override { v.visit(*this); }};
// Visitante concretoclass AreaVisitor : public Visitor {public: void visit(Circle& c) override { std::cout << "Circle area: " << 3.14159 * c.radius * c.radius << ""; } void visit(Rectangle& r) override { std::cout << "Rectangle area: " << r.width * r.height << ""; }};
int main() { std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back(std::make_unique<Circle>()); shapes.push_back(std::make_unique<Rectangle>());
AreaVisitor av; for (auto& s : shapes) { s->accept(av); } return 0;}2. Patrón Strategy (Estrategia)
Strategy permite cambiar el algoritmo de procesamiento de datos durante la ejecución. A diferencia de Visitor, que trabaja con una jerarquía de objetos, Strategy encapsula un algoritmo individual.
Escenario real
Sistema de compresión de archivos. El usuario selecciona un algoritmo: ZIP, GZIP o propio. En lugar de una cadena if-else, usamos Strategy.
Implementación con std::function
#include <iostream>#include <functional>#include <string>
class CompressionContext {private: std::function<void(const std::string&)> strategy_;public: explicit CompressionContext(std::function<void(const std::string&)> strategy) : strategy_(std::move(strategy)) {}
void setStrategy(std::function<void(const std::string&)> strategy) { strategy_ = std::move(strategy); }
void compress(const std::string& data) { if (strategy_) { strategy_(data); } else { std::cout << "No strategy set"; } }};
int main() { auto zip = [](const std::string& d) { std::cout << "Compressing with ZIP: " << d << ""; }; auto gzip = [](const std::string& d) { std::cout << "Compressing with GZIP: " << d << ""; };
CompressionContext ctx(zip); ctx.compress("Hello World"); // ZIP
ctx.setStrategy(gzip)