О чем говорит принцип open/closed?

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

Ответ

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

  • Закрыты для модификации: Стабильный, протестированный код не должен изменяться при добавлении новой функциональности, чтобы не рисковать внесением ошибок.
  • Открыты для расширения: Новая функциональность добавляется путем создания новых сущностей (через наследование, композицию, шаблоны), которые расширяют поведение существующих.

Пример нарушения OCP в C++:

class Logger {
public:
    void log(const std::string& message, const std::string& type) {
        if (type == "FILE") {
            // запись в файл
        } else if (type == "CONSOLE") {
            // вывод в консоль
        }
        // Добавление нового типа (например, "NETWORK") требует МОДИФИКАЦИИ этого класса!
    }
};

Пример соблюдения OCP с использованием полиморфизма:

// Абстрактный базовый класс, закрытый для модификации
class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void log(const std::string& message) = 0;
};

// Конкретные реализации, открытые для расширения
class FileLogger : public ILogger {
public:
    void log(const std::string& message) override { /* запись в файл */ }
};

class ConsoleLogger : public ILogger {
public:
    void log(const std::string& message) override { /* вывод в консоль */ }
};

// НОВЫЙ тип логгера можно добавить БЕЗ изменения существующего кода
class NetworkLogger : public ILogger {
public:
    void log(const std::string& message) override { /* отправка по сети */ }
};

// Клиентский код работает с абстракцией
void process(ILogger& logger) {
    logger.log("Operation completed");
}

Таким образом, для добавления нового способа логирования мы создаем новый класс, а не меняем старые, что снижает риск регрессий и повышает поддерживаемость кода.