Следующий пример иерархии наследования поможет прояснить эти понятия:

Рис. 17.3.  Виды летательных аппаратов

Рассмотрим вызов:

my_aircraft.lower_landing_gear

Вопрос о типизации: когда убедиться, что здесь будет компонент lower_landing_gear ('выпустить шасси'), применимый к объекту (для COPTER его не будет вовсе) Вопрос о связывании: какую из нескольких возможных версий выбрать.

Статическое связывание означало бы, что мы игнорируем тип присоединяемого объекта и полагаемся на объявление сущности. В итоге, имея дело с Boeing 747-400, мы вызвали бы версию, разработанную для обычных лайнеров серии 747, а не для их модификации 747-400. Динамическое связывание применяет операцию, требуемую объектом, и это правильный подход.

При статической типизации компилятор не отклонит вызов, если можно гарантировать, что при выполнении программы к сущности my_aircraft будет присоединен объект, поставляемый с компонентом, соответствующим lower_landing_gear. Базисная техника получения гарантий проста: при обязательном объявлении my_aircraft требуется, чтобы базовый класс его типа включал такой компонент. Поэтому my_aircraft не может быть объявлен как AIRCRAFT, так как последний не имеет lower_landing_gear на этом уровне; вертолеты, по крайней мере в нашем примере, выпускать шасси не умеют. Если же мы объявим сущность как PLANE, - класс, содержащий требуемый компонент, - все будет в порядке.

Динамическая типизация в стиле Smalltalk требует дождаться вызова, и в момент его выполнения проверить наличие нужного компонента. Такое поведение возможно для прототипов и экспериментальных разработок, но недопустимо для промышленных систем - в момент полета поздно спрашивать, есть ли у вас шасси.

Ковариантность и скрытие потомком

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

Но мир не прост. Объединение статической типизации с некоторыми требованиями программной инженерии создает проблемы более сложные, чем это кажется с первого взгляда. Проблемы вызывают два механизма: ковариантность (covariance) - смена типов параметров при переопределении, скрытие потомком (descendant hiding) - способность класса потомка ограничивать статус экспорта наследуемых компонентов.

Ковариантность

Что происходит с аргументами компонента при переопределении его типа? Это важнейшая проблема, и мы уже видели ряд примеров ее проявления: устройства и принтеры, одно- и двухсвязные списки и т. д. (см. разделы 16.6, 16.7).

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

Представим себе готовящуюся к чемпионату лыжную команду университета. Класс GIRL включает лыжниц, выступающих в составе женской сборной, BOY - лыжников. Ряд участников обеих команд ранжированы, показав хорошие результаты на предыдущих соревнованиях. Это важно для них, поскольку теперь они побегут первыми, получив преимущество перед остальными. (Это правило, дающее привилегии уже привилегированным, возможно и делает слалом и лыжные гонки столь привлекательными в глазах многих людей, являясь хорошей метафорой самой жизни.) Итак, мы имеем два новых класса: RANKED_GIRL и RANKED_BOY.

Рис. 17.4.  Классификация лыжников

Для проживания спортсменов забронирован ряд номеров: только для мужчин, только для девушек, только для девушек-призеров. Для отображения этого используем параллельную иерархию классов: ROOM, GIRL_ROOM и RANKED_GIRL_ROOM.

Вот набросок класса SKIER:

class SKIER feature

roommate: SKIER

-- Сосед по номеру.

share (other: SKIER) is

-- Выбрать в качестве соседа other.

require

other /= Void

do

roommate := other

end

... Другие возможные компоненты, опущенные в этом и последующих классах ...

end

Нас интересуют два компонента: атрибут roommate и процедура share, 'размещающая' данного лыжника в одном номере с текущим лыжником:

s1, s2: SKIER

...

s1.share (s2)

При объявлении сущности other можно отказаться от типа SKIER в пользу закрепленного типа like roommate (или like Current для roommate и other одновременно). Но давайте забудем на время о закреплении типов (мы к ним еще вернемся) и посмотрим на проблему ковариантности в ее изначальном виде.

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату