Паттерны проектирования в C++ с примерами: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в C++ с примерами: от А до Я

Паттерны проектирования (design patterns) — это проверенные решения типовых задач, возникающих при разработке программного обеспечения. Для C++ эти шаблоны особенно важны, так как язык сочетает объектно-ориентированное, обобщённое и низкоуровневое программирование. В этой статье мы разберём ключевые паттерны проектирования в C++ с примерами кода, которые помогут вам писать более гибкий, переиспользуемый и поддерживаемый код.

Мы рассмотрим три основные категории паттернов по классификации GoF (Gang of Four): порождающие, структурные и поведенческие. Каждый паттерн будет проиллюстрирован реальным примером на C++.

1. Порождающие паттерны (Creational Patterns)

Порождающие паттерны абстрагируют процесс создания объектов, делая систему независимой от того, как объекты создаются, компонуются и представляются.

1.1. Singleton (Одиночка)

Singleton гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В C++ часто реализуется через статический локальный объект (thread-safe с C++11).

class Logger {
public:
    static Logger& getInstance() {
        static Logger instance; // thread-safe инициализация
        return instance;
    }

    void log(const std::string& message) {
        std::cout << "[LOG] " << message << std::endl;
    }

private:
    Logger() = default;
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
};

// Использование
int main() {
    Logger::getInstance().log("Приложение запущено");
    return 0;
}

Когда использовать: управление конфигурацией, пулы потоков, системы логирования.

1.2. Factory Method (Фабричный метод)

Factory Method определяет интерфейс для создания объекта, но позволяет подклассам решать, какой класс инстанциировать.

class Product {
public:
    virtual void use() = 0;
    virtual ~Product() = default;
};

class ConcreteProductA : public Product {
public:
    void use() override { std::cout << "Продукт A" << std::endl; }
};

class ConcreteProductB : public Product {
public:
    void use() override { std::cout << "Продукт B" << std::endl; }
};

class Creator {
public:
    virtual Product* createProduct() = 0;
    void someOperation() {
        Product* p = createProduct();
        p->use();
        delete p;
    }
    virtual ~Creator() = default;
};

class CreatorA : public Creator {
public:
    Product* createProduct() override { return new ConcreteProductA(); }
};

class CreatorB : public Creator {
public:
    Product* createProduct() override { return new ConcreteProductB(); }
};

Когда использовать: когда класс не знает, объекты каких подклассов ему нужно создавать.

2. Структурные паттерны (Structural Patterns)

Структурные паттерны описывают, как классы и объекты могут быть объединены в более крупные структуры, сохраняя при этом гибкость и эффективность.

2.1. Adapter (Адаптер)

Adapter позволяет объектам с несовместимыми интерфейсами работать вместе. В C++ часто реализуется через наследование или композицию.

// Целевой интерфейс
class Target {
public:
    virtual void request() const {
        std::cout << "Обычный запрос" << std::endl;
    }
    virtual ~Target() = default;
};

// Адаптируемый класс
class Adaptee {
public:
    void specificRequest() const {
        std::cout << "Специфический запрос" << std::endl;
    }
};

// Адаптер (через композицию)
class Adapter : public Target {
private:
    Adaptee* adaptee_;
public:
    Adapter(Adaptee* a) : adaptee_(a) {}
    void request() const override {
        adaptee_->specificRequest();
    }
};

// Использование
int main() {
    Adaptee* adaptee = new Adaptee();
    Target* target = new Adapter(adaptee);
    target->request(); // Выведет: Специфический запрос
    delete target;
    delete adaptee;
    return 0;
}

Когда использовать: интеграция с legacy-кодом, работа с библиотеками с несовместимыми API.

2.2. Composite (Компоновщик)

Composite позволяет сгруппировать объекты в древовидные структуры и работать с ними как с единичными объектами.

#include <vector>

class Component {
public:
    virtual void operation() const = 0;
    virtual void add(Component*) {}
    virtual void remove(Component*) {}
    virtual ~Component() = default;
};

class Leaf : public Component {
public:
    void operation() const override {
        std::cout << "Лист" << std::endl;
    }
};

class Composite : public Component {
private:
    std::vector<Component*> children_;
public:
    void add(Component* c) override { children_.push_back(c); }
    void operation() const override {
        std::cout << "Композит (";
        for (const auto& child : children_) {
            child->operation();
        }
        std::cout << ")" << std::endl;
    }
};

Когда использовать: работа с иерархиями (файловая система, GUI-виджеты).

3. Поведенческие паттерны (Behavioral Patterns)

Поведенческие паттерны определяют алгоритмы и способы взаимодействия между объектами.

3.1. Observer (Наблюдатель)

Observer определяет зависимость "один ко многим" между объектами так, что при изменении состояния одного объекта все зависимые объекты уведомляются и обновляются автоматически.

#include <vector>
#include <algorithm>

class Observer {
public:
    virtual void update(const std::string& message) = 0;
    virtual ~Observer() = default;
};

class Subject {
private:
    std::vector<Observer*> observers_;
public:
    void attach(Observer* o) { observers_.push_back(o); }
    void detach(Observer* o) {
        observers_.erase(std::remove(observers_.begin(), observers_.end(), o), observers_.end());
    }
    void notify(const std::string& msg) {
        for (auto* obs : observers_) {
            obs->update(msg);
        }
    }
};

class ConcreteObserver : public Observer {
private:
    std::string name_;
public:
    ConcreteObserver(const std::string& name) : name_(name) {}
    void update(const std::string& message) override {
        std::cout << name_ << " получил: "                

Блоги

Рекомендаций по книгам