Читаем Основы объектно-ориентированного программирования полностью

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

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

<p>Ковариантность и скрытие потомком</p>

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

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

<p>Ковариантность</p>

Что происходит с аргументами компонента при переопределении его типа? Это важнейшая проблема, и мы уже видели ряд примеров ее проявления: устройства и принтеры, одно- и двухсвязные списки и т. д. (см. разделы 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 одновременно). Но давайте забудем на время о закреплении типов (мы к ним еще вернемся) и посмотрим на проблему ковариантности в ее изначальном виде.

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

class GIRL inherit

SKIER

redefine roommate end

feature

roommate: GIRL

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

end

Переопределим, соответственно, и аргумент процедуры share. Более полный вариант класса теперь выглядит так:

class GIRL inherit

SKIER

redefine roommate, share end

feature

roommate: GIRL

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

Перейти на страницу:

Похожие книги

«Ага!» и его секреты
«Ага!» и его секреты

Вы бы не хотели, скажем, изобрести что-то или открыть новый физический закон, а то и сочинить поэму или написать концерт для фортепьяно с оркестром?Не плохо бы, верно? Только как это сделать? Говорят, Шиллер уверял, будто сочинять стихи ему помогает запах гнилых яблок. И потому, принимаясь за работу, всегда клал их в ящик письменного стола. А физик Гельмгольц поступал иначе. Разложив все мысленно по полочкам, он дожидался вечера и медленно поднимался на гору лесной дорогой. Во время такой прогулки приходило нужное решение.Словом, сколько умов, столько способов заставить мозг работать творчески. А нет ли каких-то строго научных правил? Одинаковы ли они для математиков, биологов, инженеров, поэтов, художников? Да и существуют ли такие приемы, или каждый должен полагаться на свои природные способности и капризы вдохновения?Это тем более важно знать, что теперь появились «электронные ньютоны» — машины, специальность которых делать открытия. Но их еще нужно учить.Решающее слово здесь принадлежит биологам: именно они должны давать рецепты инженерам. А биологи и сами знают о том, как мы думаем, далеко не все. Им предстоит еще активнее исследовать лабораторию нашего мышления.О том, как ведутся эти исследования, как постепенно «умнеют» машины, как они учатся и как их учат, — словом, о новой науке эвристике рассказывает эта книга.

Елена Викторовна Сапарина

Зарубежная компьютерная, околокомпьютерная литература