Padrões Avançados de Projeto em C++: da Teoria ao Código Real
Padrões de projeto são soluções comprovadas para problemas típicos em programação orientada a objetos. Se você já dominou os padrões básicos (Singleton, Factory, Observer), é hora de avançar para o próximo nível. Neste artigo, vamos analisar quatro padrões avançados: Visitor, Strategy, State e Command. Todos os exemplos são escritos em C++17/20 moderno, usando smart pointers, polimorfismo e templates.
1. Padrão Visitor (Visitante)
O padrão Visitor permite adicionar novas operações a uma hierarquia de classes sem modificar as próprias classes. É uma solução ideal para trabalhar com árvores AST, cenas gráficas ou sistemas de exportação.
Problema
Você tem uma hierarquia de formas geométricas (Circle, Rectangle, Triangle). Precisa implementar operações: cálculo de área, renderização, serialização em JSON. Se adicionar métodos em cada classe, viola o princípio aberto/fechado.
Solução em 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. Padrão Strategy (Estratégia)
Strategy permite alterar o algoritmo de processamento de dados durante a execução. Diferente do Visitor, que trabalha com hierarquia de objetos, o Strategy encapsula um algoritmo individual.
Cenário real
Sistema de compressão de arquivos. O usuário escolhe o algoritmo: ZIP, GZIP ou próprio. Em vez de uma cadeia if-else, usamos Strategy.
Implementação com 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)