Padrões de Projeto Avançados em C++: da Teoria às Implementações Complexas
\n\nPadrões de projeto são soluções comprovadas para problemas típicos que surgem no desenvolvimento de software. Se os padrões básicos (Singleton, Factory, Observer) são familiares a todo desenvolvedor, as técnicas avançadas exigem um profundo entendimento da linguagem C++, seus templates, herança múltipla e metaprogramação. Neste artigo, analisaremos quatro padrões poderosos: Visitor, Strategy, State e CRTP — com exemplos reais de código e explicação de quando aplicá-los.
\n\n1. Padrão Visitor (Visitante) — extensão de funcionalidade sem alterar classes
\n\nO padrão Visitor permite adicionar novas operações a uma hierarquia de classes sem modificar as próprias classes. Isso é especialmente útil quando você tem uma estrutura de objetos estável, mas algoritmos de processamento que mudam com frequência.
\n\nExemplo: processamento de formas geométricas
\n\n#include <iostream>\n#include <vector>\n#include <memory>\n\n// Forward declaration\nclass Circle;\nclass Rectangle;\n\n// Visitante abstrato\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 abstrato\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\nQuando usar: quando você precisa executar diferentes operações em objetos de uma hierarquia complexa sem modificar seu código. Desvantagem — adicionar um novo tipo requer alterar todos os visitantes.
\n\n2. Padrão Strategy (Estratégia) — troca flexível de algoritmos em tempo real
\n\nStrategy encapsula uma família de algoritmos intercambiáveis e permite substituí-los durante a execução. Em C++, isso é frequentemente implementado via std::function ou classes virtuais.
Exemplo: sistema de ordenação de dados
\n\n#include <iostream>\n#include <vector>\n#include <algorithm>\n#include <memory>\n#include <functional>\n\n// Contexto que usa a estratégia\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// Estratégias concretas\nvoid bubbleSort(std::vector<int>& da