В чём суть принципа открытости/закрытости (Open-Closed Principle, OCP)?

«В чём суть принципа открытости/закрытости (Open-Closed Principle, OCP)?» — вопрос из категории Архитектура, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

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

Проблема (нарушение OCP):

class Logger {
    func log(message: String, to type: String) {
        if type == "console" {
            print(message)
        } else if type == "file" {
            // Запись в файл
        }
        // Добавление нового типа лога (например, "network") требует изменения этого метода!
    }
}

Решение (соблюдение OCP через абстракцию):

// Абстракция, закрытая для модификации
protocol LogDestination {
    func write(_ message: String)
}

// Расширения, открытые для добавления
class ConsoleDestination: LogDestination {
    func write(_ message: String) { print(message) }
}

class FileDestination: LogDestination {
    func write(_ message: String) { /* Запись в файл */ }
}

class NetworkDestination: LogDestination {
    func write(_ message: String) { /* Отправка по сети */ }
}

// Класс Logger теперь стабилен
class Logger {
    private let destination: LogDestination
    init(destination: LogDestination) {
        self.destination = destination
    }
    func log(message: String) {
        destination.write(message)
    }
}

// Использование
let logger = Logger(destination: NetworkDestination())
logger.log("Test message")

Выгода: Чтобы добавить новый способ логирования, мы создаем новый класс, реализующий LogDestination, не трогая существующий код Logger, ConsoleDestination или FileDestination. Это снижает риск появления новых ошибок в уже работающем коде.