Опубликован: 17.10.2005 | Доступ: свободный | Студентов: 8842 / 595 | Оценка: 4.38 / 4.10 | Длительность: 41:16:00
ISBN: 978-5-7502-0255-3
Специальности: Программист
Лекция 16:

Техника наследования

Типизация и повторное объявление

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

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

Устройства и принтеры

Вот простой пример переопределения типа. Рассмотрим понятие устройства, включив предположение о том, что для любого устройства есть альтернатива, так что устройство можно заменить, если оно по каким-либо причинам недоступно:

class DEVICE feature
         alternate: DEVICE
         set_alternate (a: DEVICE) is
                         -- Пусть a - альтернативное устройство.
                do
                         alternate := a
                end
       ... Прочие компоненты ...
end

Принтер является устройством, так что использование наследования оправдано. Но альтернативой принтера может быть только принтер, но не дисковод для компакт-дисков или сетевая карта, - поэтому мы должны переопределить тип:

Устройства и принтеры

Рис. 16.6. Устройства и принтеры
class PRINTER inherit
         DEVICE
                  redefine alternate, set_alternate
feature
         alternate: PRINTER
         set_alternate (a: PRINTER) is
                           -- Пусть a - альтернативное устройство.
                  ... Тело как у класса DEVICE ...
         ... Прочие компоненты ...
end

В этом и проявляется специализирующая природа наследования.

Одно- и двусвязные элементы

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

indexing
          description: "Односвязные элементы списка"
class LINKABLE [G] feature
          item: G
          right: LINKABLE [G]
          put_right (other: LINKABLE [G]) is
                            -- Поместить other справа от текущего элемента.
                   do right := other end
          ... Прочие компоненты ...
end
Односвязный элемент списка

Рис. 16.7. Односвязный элемент списка

Ряд приложений требуют двунаправленных списков. Класс TWO_WAY_LIST - наследник LINKED_LIST должен быть также наследником класса BI_LINKABLE, являющегося наследником класса LINKABLE.

Параллельные иерархии

Рис. 16.8. Параллельные иерархии

Двусвязный элемент списка имеет еще одно поле:

Двусвязный элемент списка

Рис. 16.9. Двусвязный элемент списка

В состав двунаправленных списков должны входить лишь двусвязные элементы (хотя последние, в силу полиморфизма, вполне можно внедрять и в однонаправленные структуры). Переопределив right и put_right, мы гарантируем однородность двусвязных списков.

indexing
         description: "Элементы двусвязного списка"
class BI_LINKABLE [G] inherit
         LINKABLE [G]
                  redefine right, put_right end
feature
         left, right: BI_LINKABLE [G]
         put_right (other: BI_LINKABLE [G]) is
                           -- Поместить other справа от текущего элемента.
do
                           right := other
                           if other /= Void then other.put_left (Current) end
                  end
         put_left (other: BI_LINKABLE [G]) is
                           -- Поместить other слева от текущего элемента.
                  ... Упражнение для читателя ...
         ... Прочие компоненты ...
invariant
         right = Void or else right.left = Current
         left = Void or else left.right = Current
end

(Попробуйте написать put_left. Здесь скрыта ловушка! См. приложение A.)

Правило повторного объявления типов

Примеры, рассмотренные выше, несмотря на все их различия, объединяет необходимость повторного объявления типов. Спуск по иерархии наследования означает специализацию классов, и в соответствии со специализацией изменяются типы функций и типы аргументов подпрограмм, как, например, a в set_alternate и other в put_right ; изменяются типы запросов - alternate и right.

Этот аспект повторного объявления выражает следующее правило:

Правило повторного объявления типов

При повторном объявлении компонента можно заменить тип компонента (для атрибутов и функций) или тип формального параметра (для подпрограмм) любым совместимым типом.

Правило использует понятие совместимости типов. Связка "или", стоящая в тексте правила, не исключает того, что при повторном объявлении функции мы можем одновременно изменить как тип результата функции, так и тип одного или нескольких ее аргументов.

Любое повторное объявление ведет к специализации, а, следовательно, к изменению типов. Так, с переходом к двунаправленным спискам параметры и результаты функций сменили свой тип на BI_LINKABLE. Отсюда становится понятен тот термин, которым часто описывают политику редекларации типов, - ковариантная типизация (covariant typing), где приставка "ко" указывает на параллельное изменение типов при спуске по диаграмме наследования.

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

Александр Шалухо
Александр Шалухо
Анатолий Садков
Анатолий Садков

При заказе pdf документа с сертификатом будет отправлен только сертификат или что-то ещё?