Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP / books / Osnovi objektno-orientirovannogo programmirovaniya.pdf
Скачиваний:
63
Добавлен:
03.03.2016
Размер:
9.04 Mб
Скачать

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

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

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

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

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

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), где приставка "ко" указывает на параллельное изменение типов при спуске по диаграмме наследования.

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

Соседние файлы в папке books