Наследование Реализаций: Похороните Стюардессу

Ключевое противоречие ООП Как известно, классический ООП держится на трёх китах:

  1. Инкапсуляция
  2. Наследование
  3. Полиморфизм
Классическая реализация по умолчанию:
  1. Инкапсуляция — публичные и частные члены класса
  2. Наследование — это реализация функциональности путем расширения одного класса-предка, защищенных членов класса.

  3. Полиморфизм — виртуальные методы класса-предка.

Но и в 1986 году была выявлена серьезная проблема , кратко сформулированный следующим образом:

Наследование нарушает инкапсуляцию

  1. Защищенные члены класса-предка доступны классу-потомку.

    Всем остальным доступен только публичный интерфейс класса.

    Крайний случай взлома — антипаттерн Паблик Морозов ;

  2. Фактически изменить поведение предка можно, только переопределив виртуальные методы;
  3. Принцип замены Лискова обязывает класс-потомок удовлетворять всем требованиям, предъявляемым к классу-предку;
  4. Для выполнения пункта 2 в строгом соответствии с пунктом 3 классу-потомку необходима полная информация о времени вызова и реализации переопределенного виртуального метода;
  5. Информация в пункте 4 зависит от реализации класса-предка, включая частные члены и их код.
В теории у нас уже есть эпический провал, а как насчет практики?
  1. Зависимость, создаваемая наследованием, чрезвычайно сильна;
  2. Наследники сверхчувствительны к любым изменениям предка;
  3. Наследование чужого кода добавляет адскую боль при обслуживании: разработчики библиотек рискуют столкнуться с препятствиями из-за нарушенной обратной совместимости при малейшем изменении базового класса, а разработчики приложений рискуют регрессировать при любом обновлении используемых ими библиотек.

Любой, кто использует фреймворки, требующие наследования от своих классов (WinForms, WPF, WebForms, ASP.NET), легко найдет подтверждение всем трем пунктам в своем опыте.

это так плохо? Теоретическое решение Влияние проблемы можно смягчить, приняв определенные конвенции: 1. Защищенные участники не нужны Это соглашение исключает морозную публику как класс.

2. Методы виртуального предка ничего не делают Это соглашение позволяет объединить знания о реализации предка с независимостью реализации в потомке.

3. Виртуальные методы предка никогда не вызываются в его коде.

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

4. Экземпляры-предки никогда не создаются.

Это соглашение позволяет нам избавиться от несоответствия между требованиями к виртуальным методам (контракт публичного класса) с одной стороны и обязательством ничего не делать (контракт защищенного класса) — с другой.

Теперь принцип подмены Лискова можно соблюдать, не вступая в порочную связь с частным содержанием предка.

5. У предка нет невиртуальных членов.

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

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

К.

?.

Д.

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

Но это все теория, и нам нужно.

Практические решения

  1. Виртуальные фиктивные методы уже существуют во многих языках и носят гордое название абстрактный .

  2. Классы, экземпляры которых не могут быть созданы, также существуют во многих языках и даже имеют одинаковые классифицировать .

  3. Полное соблюдение этих соглашений на языке C++ использовалось в качестве образца для проектирования и реализации.

    Объектная модель компонента .

  4. И самое приятное: в C# и многих других языках соглашения реализованы как первоклассный элемент. "интерфейс" .

    Происхождение названия очевидно — в результате соблюдения конвенций от класса остался только его публичный интерфейс.

    И если от обычных классов множественное наследование встречается редко, то от интерфейсов оно доступно без каких-либо ограничений.

Полученные результаты
  1. Языки, где нет наследования от классов, но есть наследование от интерфейсов (например, Go), нельзя лишить звания объектно-ориентированных.

    Более того, такая реализация ООП теоретически правильнее и безопаснее на практике.

  2. Наследование от обычных классов (имеющих реализацию) — крайне специфический и крайне опасный архаизм.

  3. Избегайте наследования реализаций без крайней необходимости.

  4. Используйте модификатор «sealed» (для .

    NET) или его эквивалент для всех классов, кроме тех, которые специально предназначены для наследования реализации.

  5. Избегайте публичных незапечатанных классов: пока наследование остается в пределах его сборок, от него все равно можно получить некоторую выгоду, а вред ограничен.

PS: Дополнения и критика традиционно приветствуются.

Теги: #объектно-ориентированное проектирование #паттерны проектирования #антипаттерны #программирование #.

NET #проектирование и рефакторинг #C++ #ООП

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.