Patrones de diseño avanzados en C++: de la teoría a implementaciones complejas
\n\nLos patrones de diseño son soluciones probadas para problemas típicos que surgen durante el desarrollo de software. Si los patrones básicos (Singleton, Factory, Observer) son conocidos por todo desarrollador, las técnicas avanzadas requieren un profundo conocimiento del lenguaje C++, sus plantillas, herencia múltiple y metaprogramación. En este artículo, analizaremos cuatro patrones potentes: Visitor, Strategy, State y CRTP — con ejemplos de código reales y explicación de cuándo utilizarlos.
\n\n1. Patrón Visitor (Visitante) — extensión de funcionalidad sin modificar clases
\n\nEl patrón Visitor permite agregar nuevas operaciones a una jerarquía de clases sin modificar las clases mismas. Esto es especialmente útil cuando tienes una estructura de objetos estable, pero algoritmos de procesamiento que cambian con frecuencia.
\n\nEjemplo: procesamiento de figuras geométricas
\n\n#include <iostream>\n#include <vector>\n#include <memory>\n\n// Forward declaration\nclass Circle;\nclass Rectangle;\n\n// Visitante abstracto\nclass Visitor {\npublic:\n virtual void visit(Circle& circle) = 0;\n virtual void visit(Rectangle& rectangle) = 0;\n virtual ~Visitor() = default;\n};\n\n// Elemento abstracto\nclass Shape {\npublic:\n virtual void accept(Visitor& visitor) = 0;\n virtual ~Shape() = default;\n};\n\n// Elementos concretos\nclass Circle : public Shape {\npublic:\n double radius;\n explicit Circle(double r) : radius(r) {}\n void accept(Visitor& visitor) override {\n visitor.visit(*this);\n }\n};\n\nclass Rectangle : public Shape {\npublic:\n double width, height;\n Rectangle(double w, double h) : width(w), height(h) {}\n void accept(Visitor& visitor) override {\n visitor.visit(*this);\n }\n};\n\n// Visitante concreto — cálculo de área\nclass AreaVisitor : public Visitor {\npublic:\n double totalArea = 0.0;\n void visit(Circle& circle) override {\n totalArea += 3.14159 * circle.radius * circle.radius;\n }\n void visit(Rectangle& rectangle) override {\n totalArea += rectangle.width * rectangle.height;\n }\n};\n\nint main() {\n std::vector<std::unique_ptr<Shape>> shapes;\n shapes.push_back(std::make_unique<Circle>(5.0));\n shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));\n\n AreaVisitor areaCalc;\n for (auto& shape : shapes) {\n shape->accept(areaCalc);\n }\n std::cout << "Total area: " << areaCalc.totalArea << std::endl;\n return 0;\n}\n\n\nCuándo usarlo: cuando necesitas realizar diferentes operaciones sobre objetos de una jerarquía compleja sin modificar su código. Desventaja: agregar un nuevo tipo requiere modificar todos los visitantes.
\n\n2. Patrón Strategy (Estrategia) — cambio flexible de algoritmos sobre la marcha
\n\nStrategy encapsula una familia de algoritmos intercambiables y permite sustituirlos en tiempo de ejecución. En C++, esto a menudo se implementa mediante std::function o clases virtuales.
Ejemplo: sistema de ordenación de datos
\n\n#include <iostream>\n#include <vector>\n#include <algorithm>\n#include <memory>\n#include <functional>\n\n// Contexto que utiliza la estrategia\nclass Sorter {\nprivate:\n std::function<void(std::vector<int>&)> strategy_;\npublic:\n explicit Sorter(std::function<void(std::vector<int>&)> strategy)\n : strategy_(std::move(strategy)) {}\n\n void setStrategy(std::function<void(std::vector<int>&)> newStrategy) {\n strategy_ = std::move(newStrategy);\n }\n\n void sort(std::vector<int>& data) {\n if (strategy_) {\n strategy_(data);\n }\n }\n};\n\n// Estrategias concretas\nvoid bubbleSort(std::vector<int>& da