Design Patterns in C++: Advanced Techniques and Code Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Advanced Design Patterns in C++: From Theory to Real Code



Design patterns are proven solutions to typical problems in object-oriented programming. If you've already mastered basic patterns (Singleton, Factory, Observer), it's time to move to the next level. In this article, we'll cover four advanced patterns: Visitor, Strategy, State, and Command. All examples are written in modern C++17/20 using smart pointers, polymorphism, and templates.



1. Visitor Pattern



The Visitor pattern allows adding new operations to a class hierarchy without modifying the classes themselves. This is an ideal solution for working with AST trees, graphic scenes, or export systems.



Problem

You have a hierarchy of geometric shapes (Circle, Rectangle, Triangle). You need to implement operations: area calculation, rendering, JSON serialization. If you add methods to each class, the open/closed principle is violated.



Solution in 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); }};

// Concrete visitorclass 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. Strategy Pattern



Strategy allows changing the data processing algorithm at runtime. Unlike Visitor, which works with an object hierarchy, Strategy encapsulates a single algorithm.



Real-world scenario

A file compression system. The user selects an algorithm: ZIP, GZIP, or custom. Instead of an if-else chain, we use Strategy.



Implementation with 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)

Blogs

Book Recommendations