Это очень интересный вопрос.Я склонен согласиться с вами в том смысле, что основной проблемой наследования является связь между родителем и его детьми и то, как эти отношения препятствуют способности родительского класса развиваться, не разрушая своих детей.
Я интерпретирую, что вы хотели спросить, является ли проблема хрупкого базового класса единственным проявлением нарушения принципа "инкапсуляции наследственных разрывов", верно?
Я верю (как и вы), что если мы нарушим инкапсуляцию при использовании наследования, то, безусловно, проявление этой проблемы сильной связи будет вхрупкость родительского класса, который сломает своих потомков, когда он изменится.
Так что, в этом смысле или интерпретации, я думаю, что вы, вероятно, правы.
Противоположное не обязательно верно, т.е.наличие хрупкого базового класса не обязательно означает, что вы нарушили правила инкапсуляции наследования.
Я внимательно прочитал ту же библиографию, что и у васи этот отрывок может пролить некоторый свет на этот вопрос.
Из шаблонов проектирования: элементы многоразового объектно-ориентированного программного обеспечения:
«родительские классы часто определяют по крайней мере часть своих подклассовфизическое представление.Поскольку наследование подвергает подкласс деталям реализации его родителя, часто говорят, что «наследование нарушает инкапсуляцию» [Sny86]. »связь.Очевидно, что хрупкий базовый класс является проявлением проблемы связывания, поэтому вы все равно можете быть на чем-то
Объект обычно имеет открытый интерфейс, который предоставляет миру контракт на то, что он может делать.Эти методы в общедоступном интерфейсе объекта должны удовлетворять ряду предварительных условий, инвариантов и постусловий для служб, предоставляемых объектом.И пользователи объекта взаимодействуют с ним на основе этого контракта.
Пользователям объекта не нужно знать детали реализации контракта.И если мы когда-либо разорвем этот контракт, пострадают все пользователи объектов.
Эта зависимость от открытого интерфейса объекта является формой связи, а при наличии связи существует форма хрупкости, которую можно изменить.
Например, водителям не нужно знать, как работает система гидравлического рулевого колеса в их автомобилях, но они все равно могут водить седан или внедорожник, как если бы они были одним и тем же.Потому что они понимают абстракцию рулевого колеса и контракт, регулирующий его общедоступный интерфейс.Но если мы когда-нибудь заменим рулевое колесо в целом, чтобы оно работало так, как будто это был погрузчик, то, вероятно, каждый разбил бы свои машины (мы нарушили открытый интерфейс рулевого колеса).
Итак, ожидается, чтооткрытый интерфейс класса должен быть довольно стабильным, и ожидается, что любые изменения там, безусловно, нарушат функциональность его клиентов.
Наследование нарушает инкапсуляцию, когда подклассы не могут просто зависеть от открытого интерфейса объекта, когда онипотребуются дополнительные знания о том, как реализован их родительский класс, чтобы обеспечить функциональную дочернюю реализацию (и поэтому в некоторых случаях делегирование является хорошей альтернативой, поскольку делегирование зависит только от открытого интерфейса объектов).
State Coupling
Это проявляется по-разному, например, вы можете нарушить инкапсуляцию, если у вас был прямой доступ к переменным состояния в родительском классе (также упоминается в статье Снайдера, которую выared).
Из инкапсуляции и наследования в языках объектно-ориентированного программирования:
"Чтобы сохранить все преимущества инкапсуляции, внешние интерфейсы определения класса не должны включать переменные экземпляра"
В статиВ языках с модификаторами доступности (например, Java или C #) такое нарушение может проявиться, если мы выставим непубличные поля из родительского класса.В динамических языках без модификаторов доступности такое нарушение проявляется, когда разработчик подкласса обращается к полю, которое предназначалось только для частного использования родительского класса (например, _field
в Python).
Считаете ли вы эту проблему доступа к полючасть хрупкой проблемы базового класса?Мне кажется, что эта форма связи наследования не была обязательно , охватываемой идеей проблемы хрупкого базового класса в известной статье, которой вы поделились.
Соединение защищенного интерфейса
Другой способ, которым это проявляется, заключается в том, что теперь родительскому классу может потребоваться предоставить новый интерфейс, отличный от открытого интерфейса, но предназначенный только для целей наследования: защищенный интерфейс.Таким образом, может потребоваться предоставить набор «защищенных» или «привилегированных» методов, которые предоставляют доступ к дополнительным деталям, обычно не предоставляемым в общедоступном интерфейсе, предоставляемом обычным пользователям объектов.
Это, очевидно, необходимо, потому что дочерние классы требуют, чтобы эти дополнительные детали обеспечивали разумную реализацию родительского класса с некоторой расширенной функциональностью или измененным поведением.
Теперь родительскому классу также потребуетсячтобы гарантировать, что этот защищенный интерфейс довольно стабилен, так как любые изменения там будут нарушать наследуемые классы почти так же, как изменения в его публичном интерфейсе сломают обычных пользователей класса.
На этом этапе мы сталкиваемся с сильной формой связи,тот, который может помешать развитию родительского класса в будущем из-за потенциальных проблем, которые он может вызвать у своих потомков.
Теперь обратите внимание, что соединение и нарушение инкапсуляции проявилось во время разработки, хрупкость базового классапоэтому также был введен здесь, даже если эта проблема соединения никогда не проявляется в коде, потому что мы никогда не вызываем изменения в родительском классе.
Итак, моя интерпретация заключается в том, что связь, введенная наследникомnce приводит к нарушению инкапсуляции, что, в свою очередь, приводит к проблемам с уязвимыми базовыми классами, которые вы описали.
В некотором смысле ваш вопрос предлагает цепочку причинно-следственных связей, где, как представляется, вы предлагаете проблему с уязвимыми базовыми классамиэто то, что нарушает наследование, но в моем случае я считаю, что все наоборот: связь между родителем и потомком нарушает инкапсуляцию, и этот высокий уровень связи проявляется в дизайне как проблема хрупкого базового класса.
Хрупкость без нарушения инкапсуляции
При этом у нас возникает вопрос, можем ли мы иметь хрупкий базовый класс, не нарушая инкапсуляцию?
Я полагаю, что мы это делаем.Дочерний класс может полностью зависеть только от открытого интерфейса родительского класса.Например, дочерний класс, возможно, предоставил совершенно новый метод, который не наследуется, и он не является частью открытого или защищенного интерфейса родителя.
Затем однажды мы добавляем новый метод в родительский класс, который имеет точно такую же сигнатуру, какую мы добавили давно в дочернем классе, но с совершенно другим намерением.
Теперь мы что-то сломали, поскольку пользователи этого объекта ожидают, что новый метод будет вести себя так, как указано в интерфейсе родительского класса, а не как реализовано в дочернем классе.
Эту ошибку может быть трудно обнаружить.В некоторых языках это может вызвать сбой в дочернем классе, в других можно предположить, что дочерняя переопределенная версия является правом на использование.
Вариант этой проблемы будет, если новый метод определен в родительском классе.Класс имел другой модификатор доступности, чем тот же метод, определенный в дочернем классе как часть независимой эволюции обоих классов.
В любом случае, эти несоответствия не обязательно нарушают инкапсуляцию, но они делают хрупкие отношения родитель / потомок из-за связи, введенной наследованием, верно?
Другими словами, родительский класс хрупок, несмотря на то, что в этом случае между родительским и дочерним классами все в порядке.
Нарушение инкапсуляции с наследованием действительно приводит к хрупкому базовому классу, но, насколько я могу судить, многие хрупкие базовые классы не обязательно подразумевают проблему инкапсуляции в отношениях наследования.