Ваш интересный вопрос. Я склонен согласиться с вами: главная проблема наследования - это связь между родителем и его дочерними элементами. Эти отношения препятствуют развитию родительского класса, не нарушая его дочерних классов.
Я понимаю, что вы хотели спросить, является ли проблема хрупкого базового класса единственным проявлением нарушения принципа «наследование нарушает инкапсуляцию», верно?
TL; DR;
Я считаю (как и вы), что если мы нарушим инкапсуляцию при использовании наследования, то, несомненно, проявление этой проблемы сильной связи будет очевидным в хрупкости родительского класса, который ломает свои дочерние элементы, когда он изменяется.
Так что в этом смысле или интерпретации я думаю, что вы, вероятно, правы.
Обратное не обязательно верно, т.е. наличие хрупкого базового класса не обязательно означает, что вы нарушили правила инкапсуляции наследования.
Проблема в сцеплении
Я внимательно прочитал ту же библиографию, которую вы предоставили, и этот отрывок может пролить некоторый свет на этот вопрос.
Из Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения:
«Родительские классы часто определяют по крайней мере часть физического представления своих подклассов. Поскольку наследование предоставляет подклассу сведения о реализации его родителя, часто говорят, что «наследование нарушает инкапсуляцию» [Sny86] ».
Итак, похоже, что GoF подразумевал здесь, что фундаментальная проблема заключается в связи. Ясно, что хрупкий базовый класс - это проявление проблемы сцепления, поэтому вы все еще можете что-то
У объекта обычно есть общедоступный интерфейс, который предоставляет миру договор о том, что он может делать. Эти методы в общедоступном интерфейсе объекта должны удовлетворять нескольким предварительным условиям, инвариантам и постусловиям для услуг, которые предоставляет объект. Таким образом, пользователи объекта взаимодействуют с ним на основе этого контракта.
Пользователям объекта не обязательно знать детали реализации контракта. Так что, если мы когда-нибудь нарушим этот контракт, пострадают все пользователи объекта.
Эта зависимость от общедоступного интерфейса объекта является формой сцепления, а когда есть взаимосвязь, возникает форма хрупкости к изменениям.
Например, водителям не нужно знать, как работает гидравлическая система рулевого колеса в их автомобилях, но они все равно могут управлять седаном или внедорожником, как если бы они были одним и тем же, потому что они понимают абстракцию рулевого колеса и контракта. который управляет его общедоступным интерфейсом. Однако, если мы когда-нибудь изменим рулевое колесо, в целом, чтобы оно работало так, как если бы оно было рулевым погрузчиком, то, вероятно, каждый разбил бы свои машины (мы сломали общедоступный интерфейс рулевого колеса).
Таким образом, ожидается, что открытый интерфейс класса будет довольно стабильным, и ожидается, что любые изменения в нем обязательно нарушат функциональность его клиентов.
Наследование прерывает инкапсуляцию, когда подклассы не могут просто зависеть от открытого интерфейса объекта, когда им требуются дополнительные знания о том, как реализован их родительский класс, чтобы обеспечить функциональную дочернюю реализацию (и именно поэтому делегирование является хорошей альтернативой в некоторых случаях, поскольку делегирование просто зависит от публичного интерфейса объектов).
Связь состояний
Это проявляется по-разному, например, вы можете нарушить инкапсуляцию, если бы у вас был прямой доступ к переменным состояния в родительском классе (также упоминается в статье Снайдера, которой вы поделились).
Из инкапсуляции и наследования в объектно-ориентированных языках программирования:
«Чтобы сохранить все преимущества инкапсуляции, внешние интерфейсы определения класса не должны включать переменные экземпляра»
В статических языках с модификаторами доступности (например, Java или C #) такое нарушение может проявиться, если мы предоставим непубличные поля из родительского класса. В динамических языках без модификаторов доступности такое нарушение проявляется, когда разработчик подкласса обращается к полю, которое предназначалось только для частного использования родительского класса (например, _field
в Python).
Считаете ли вы эту проблему доступа к полю частью проблемы хрупкого базового класса? Мне кажется, эта форма наследования не была обязательно охвачена идеей хрупкой проблемы базового класса из известной статьи, которой вы поделились.
Защищенное интерфейсное соединение
Другой способ это проявляется в том, что теперь родительскому классу может потребоваться предоставить новый интерфейс, отличный от общедоступного, но предназначенный только для целей наследования: защищенный интерфейс. Таким образом, может потребоваться предоставить набор «защищенных» или «привилегированных» методов, которые предоставляют доступ к дополнительным сведениям, которые обычно не отображаются в общедоступном интерфейсе, предоставляемом для обычных пользователей объекта.
Это, очевидно, необходимо, потому что дочерним классам требуются эти дополнительные детали, чтобы иметь возможность обеспечить разумную реализацию родительского класса с некоторыми расширенными функциями или измененным поведением.
Теперь родительский класс также должен будет гарантировать, что этот защищенный интерфейс довольно стабилен, поскольку любые изменения в нем нарушат наследование классов в значительной степени, так как изменения в его общедоступном интерфейсе нарушат работу обычных пользователей класса.
На этом этапе мы сталкиваемся с сильной формой связывания, которая может помешать родительскому классу развиваться в будущем из-за потенциальных проблем, которые он может вызвать у его дочерних классов.
Теперь обратите внимание, что связывание и нарушение инкапсуляции проявились во время разработки, поэтому хрупкость базового класса также была здесь представлена, даже если эта проблема связывания никогда не проявляется в коде, потому что мы никогда не вызываем изменения в родительском классе.
Итак, моя интерпретация состоит в том, что связь, введенная наследованием, приводит к нарушению инкапсуляции, что, в свою очередь, приводит к проблемам хрупкого базового класса, которые вы описали.
В каком-то смысле ваш вопрос, кажется, предлагает цепочку причинно-следственных связей, где кажется, что вы предполагаете, что проблема хрупкого базового класса - это то, что нарушает наследование, но в моем случае я считаю, что это наоборот: связь между родительским и дочерним элементами нарушает инкапсуляцию и этот высокий уровень сцепления проявляется в дизайне как хрупкая проблема базового класса.
Хрупкость без повреждения инкапсуляции
При этом у нас возникает вопрос, можем ли мы иметь хрупкий базовый класс без нарушения инкапсуляции?
Я верю, что да. Дочерний класс может полностью зависеть только от открытого интерфейса родительского класса. Например, дочерний класс мог предоставить совершенно новый метод, который не наследуется и не является частью родительских общедоступных или защищенных интерфейсов.
Затем однажды мы добавляем новый метод в родительский класс, который имеет ту же сигнатуру, что мы давно добавили в дочерний класс, но с совершенно другим намерением.
Теперь мы что-то сломали, поскольку пользователи этого объекта ожидали бы, что новый метод будет вести себя так, как указано в интерфейсе родительского класса, а не как реализовано в дочернем классе.
Эту ошибку может быть сложно отловить. В некоторых языках это может вызвать сбой в дочернем классе; в других случаях можно было предположить, что права на использование имеет замещенная дочерняя версия.
Вариантом этой проблемы было бы, если бы новый метод, определенный в родительском классе, имел модификатор доступности, отличный от того же самого метода, определенного в дочернем классе как часть независимой эволюции обоих классов.
В любом случае, эти несоответствия не обязательно нарушали инкапсуляцию, но они действительно делали отношения родитель / потомок хрупкими из-за сцепления, введенного наследованием, верно?
Другими словами, родительский класс хрупок, несмотря на то, что в этом случае инкапсуляция между родительским и дочерним классами прекрасна.
Нарушение инкапсуляции с наследованием действительно вызывает хрупкий базовый класс, но хрупкий базовый класс, насколько я могу судить, не обязательно подразумевает проблему инкапсуляции в отношениях наследования.
person
Edwin Dalorzo
schedule
04.07.2018