Опубликован: 04.04.2012 | Доступ: свободный | Студентов: 1989 / 61 | Оценка: 4.60 / 4.40 | Длительность: 13:49:00
Лекция 4:

Контракты и наследование

< Лекция 3 || Лекция 4: 123 || Лекция 5 >

От множественного наследования к повторному наследованию

Необходимо проанализировать один технический случай — повторное наследование, возникающее в результате множественного наследования, когда от потомка идет несколько путей

к предку:

 Повторное наследование

Рис. 3.5. Повторное наследование

Такие структуры возникают при продвинутой разработке. Примеры можно найти в библиотеке EiffelBase. Правило следует знать, поскольку повторное наследование всегда возникает при множественном наследовании, хотя бы за счет того, что у всех классов существует общий предок — ANY.

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

Для случая, изображенного на рисунке, первый вопрос, который следует задать относительно f — компонента из класса А : в классе D ему должны соответствовать два компонента или один? Ответ прост и соответствует духу предыдущего обсуждения.

  • Если компонент приходит с обеих сторон под тем же самым именем, то есть он не переименовывался на всем пути наследования, оставаясь известным как компонент f, то ясно, что речь идет об одном и том же компоненте. Это случай допустимого конфликта имен: хотя родители имеют компоненты с одним и тем же именем, проблемы не возникает, поскольку речь идет об одном и том же компоненте. Этот случай известен как разделение компонента или склеивание.
  • Если же компонент наследуется под двумя различными именами — как результат переименования на пути наследования, — то снова, во избежание любой перегрузки, он должен обозначать два различных компонента. Мы говорим в этом случае о репликации компонента.

Очевидное ограничение применимо к случаю разделения. Если на одном из путей встретилось переобъявление, то версия компонента на другом пути должна быть отложенной (исходно или в результате отказа от определения). Если же на обоих путях мы сталкиваемся с двумя эффективными методами и хотим сохранить обе версии, то одну из них нужно переименовать, возвращаясь назад к случаю репликации.

При репликации может возникнуть неоднозначность, когда встречается переопределение. Такая ситуация может встретиться, например, при повторном наследовании от ANY, если один из классов вдоль пути наследования вводит свое собственное понятие копирования и эквивалентности, переопределив copy и is_equal из класса ANY :

 Необходимость "select"

Рис. 1.6. Необходимость "select"

Методы copy и is_equal должны всегда переопределяться совместно, поскольку постусловие copy(other) устанавливает is_equal(other). Любая операция копирования должна убеждаться, что результат эквивалентен цели копирования в соответствии с локальным определением эквивалентности.

Класс LIST переопределил copy и is_equal, чтобы быть уверенным, что копируется не заголовок списка, а его содержимое (большинство контейнерных классов подобным же образом определяют понятие копирования и сравнения).

Теперь D наследует эти методы от LIST, а также от класса C, который сохранил версии по умолчанию, наследованные от ANY. Все это работает в соответствии с предыдущими правилами. Класс D должен переименовать компоненты во избежание конфликта имен, чтобы они дублировались.

Проблема возникает в связи с полиморфизмом. Рассмотрим a типа ANY и dl типа D. Выполним присваивание, а потом вызов:

a : = dl
a.copy (...)

В этом случае при динамическом связывании возникает неоднозначность: для родительского метода copy у потомка имеются две реализации. Возникает вопрос: следует ли для связывания использовать версию LIST (известную как copy в классе) или С-версию ( Ccopy )? Ситуация возникает всякий раз, когда одновременно встречается репликация и переопределение. Для устранения неоднозначности требуется ввести предложение select. Вот как следует записать класс D :

class D inherit
  LIST [T] select copy, is_equal end
  C rename copy as C_copy, is_equal as C_is_equal end
feature
   ... Остаток текста класса...
end

Эта запись говорит, что для полиморфной цели с возможной неоднозначностью при динамическом связывании следует выбирать версию из LIST. Без предложения select в таких ситуациях не обойтись.

Конечно, вы могли бы остановить свой выбор на другой ветви либо некоторые компоненты выбирать от одного родителя, другие — от другого.

< Лекция 3 || Лекция 4: 123 || Лекция 5 >
Надежда Александрова
Надежда Александрова

Уточните пожалуйста, какие документы для этого необходимо предоставить с моей стороны. Курс "Объектно-ориентированное программирование и программная инженения".