Нарушает ли принцип подстановки Лисков (LSP) помещение NavigationController в стек другого NavigationController?

«Нарушает ли принцип подстановки Лисков (LSP) помещение NavigationController в стек другого NavigationController?» — вопрос из категории ООП, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, это прямое нарушение LSP. Принцип гласит, что объекты подклассов должны быть заменяемы на объекты родительских классов без изменения корректности программы.

UINavigationController является подклассом UIViewController. По логике, мы должны иметь возможность поместить любой UIViewController в стек навигации. Однако UINavigationController нарушает контракт базового класса:

  1. Нарушение ожидаемого поведения: Вложенный UINavigationController создаст собственную панель навигации внутри чужой, что приведет к путанице (две панели навигации), нестандартным жестам и сложностям с кнопкой "Назад".
  2. Нарушение инвариантов: Клиентский код, работающий со стеком UIViewController, не ожидает, что внутри может быть контроллер, который сам управляет своим стеком.

Правильные альтернативы:

  • Использовать обычные UIViewController:

    // Вместо вложенного NavigationController
    let detailVC = DetailViewController()
    navigationController?.pushViewController(detailVC, animated: true)
  • Использовать контейнерные контроллеры (UIViewController.addChild): Для сложных интерфейсов.

  • Использовать координаторы/роутеры: Вынести логику навигации из вью-контроллеров в отдельный слой.

  • Модальный презент: Если нужен независимый поток, представьте новый UINavigationController модально.

    let modalFlow = UINavigationController(rootViewController: StartViewController())
    present(modalFlow, animated: true)

Нарушение LSP здесь ведет к хрупкому, труднотестируемому и непредсказуемому коду.