Что такое принципы SOLID в контексте C++?

«Что такое принципы SOLID в контексте C++?» — вопрос из категории ООП, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

SOLID — это акроним пяти ключевых принципов объектно-ориентированного проектирования и программирования, направленных на создание понятного, гибкого и поддерживаемого кода. В C++ они применяются особенно важно из-за сложности языка.

1. Принцип единственной ответственности (Single Responsibility Principle - SRP) Класс должен иметь одну и только одну причину для изменения (отвечать за одну задачу).

// Нарушение SRP: класс занимается и логикой, и выводом.
class Report {
    std::string data;
public:
    void generate() { /* ...логика формирования... */ }
    void printToConsole() { std::cout << data; } // Причина для изменения №2
};

// Соблюдение SRP:
class ReportGenerator { void generate() { /* ... */ } };
class ConsoleReporter { void print(const std::string&) { /* ... */ } };

2. Принцип открытости/закрытости (Open/Closed Principle - OCP) Классы должны быть открыты для расширения (новый функционал) и закрыты для модификации (не трогаем существующий код).

// Базовый класс закрыт для модификации.
class Shape {
public:
    virtual double area() const = 0; // Виртуальный метод для расширения.
    virtual ~Shape() = default;
};

// Новый функционал добавляется новыми классами.
class Circle : public Shape { /* реализация area() */ };
class Square : public Shape { /* реализация area() */ };
// Можно добавить Triangle, не меняя код Shape или существующих функций.

3. Принцип подстановки Барбары Лисков (Liskov Substitution Principle - LSP) Объекты производного класса должны быть заменяемы на объекты базового класса, не нарушая работу программы.

// Нарушение LSP: Квадрат, наследующий Прямоугольник.
class Rectangle {
protected:
    int width, height;
public:
    virtual void setWidth(int w) { width = w; }
    virtual void setHeight(int h) { height = h; }
};
class Square : public Rectangle {
public:
    void setWidth(int w) override { width = height = w; } // Меняет и высоту!
    // Это сломает код, рассчитывающий на независимость width/height.
};

4. Принцип разделения интерфейса (Interface Segregation Principle - ISP) Много специализированных интерфейсов лучше одного общего. Клиент не должен зависеть от методов, которые он не использует.

// Плохо: Один "толстый" интерфейс.
class IMultiFunctionDevice {
public:
    virtual void print() = 0;
    virtual void scan() = 0;
    virtual void fax() = 0; // Старому принтеру это не нужно!
};

// Хорошо: Разделенные интерфейсы.
class IPrinter { virtual void print() = 0; };
class IScanner { virtual void scan() = 0; };
class IFax { virtual void fax() = 0; };
class SimplePrinter : public IPrinter { /* реализует только print */ };

5. Принцип инверсии зависимостей (Dependency Inversion Principle - DIP) Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей, детали должны зависеть от абстракций.

// Нарушение DIP: Высокоуровневый класс зависит от конкретной БД.
class HighLevelModule {
    MySQLDatabase db; // Прямая зависимость от детали.
public:
    void saveData() { db.save(); }
};

// Соблюдение DIP:
class IDatabase { // Абстракция
public:
    virtual void save(const Data&) = 0;
    virtual ~IDatabase() = default;
};
class HighLevelModule {
    std::unique_ptr<IDatabase> db; // Зависимость от абстракции.
public:
    HighLevelModule(std::unique_ptr<IDatabase> dbPtr) : db(std::move(dbPtr)) {}
    void saveData() { db->save(someData); }
};
// Теперь можно передать MySQLDatabase, PostgreSQLDatabase или MockDatabase.

Следование SOLID в C++ облегчает модульное тестирование, рефакторинг и снижает связность компонентов.

Видео-ответы