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

1

p:= r; r := p

2

p := r; x := p.diagonal

В (1) запрещается присваивать многоугольник сущности-прямоугольнику, хотя во время выполнения так получилось, что этот многоугольник является прямоугольником (аналогично тому, как можно отказаться принять собаку из-за того, что на клетке написано 'животное'). В (2) компонент diagonal оказался не применим к p несмотря на то, что во время выполнения он, фактически, присутствует.

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

На практике, случаи вида (1) и (2) маловероятны. Присваивания типа p:= r обычно встречаются внутри некоторых управляющих структур, которые зависят от условий, определяемых во время выполнения, например, от ввода данных пользователем. Более реалистичная полиморфная схема может выглядеть так:

create r.make (...); ...

screen.display_icons -- Вывод значков для разных многоугольников

screen.wait_for_mouse_click -- Ожидание щелчка кнопкой мыши

x := screen.mouse_position -- Определение места нажатия кнопки

chosen_icon := screen.icon_where_is (x) -- Определение значка,

-- на котором находится указатель мыши

if chosen_icon = rectangle_icon then

p := r

elseif ...

p := 'Многоугольник другого типа' ...

end

... Использование p, например, p.display, p.rotate, ...

В последней строке p может обозначать любой многоугольник, поэтому можно к нему применять только общие компоненты из класса POLYGON. Понятно, что операции, подходящие для прямоугольников, такие как diagonal, должны применяться только к r (например, в первом предложении if). Если придется использовать p в операторах, следующих за оператором if, то к нему могут применяться лишь операции, применимые ко всем видам многоугольников.

В другом типичном случае p просто является формальным параметром процедуры:

some_routine (p: POLYGON) is ...

и можно выполнять вызов some_routine (r), корректный в соответствии с правилом согласования типов. Но при написании процедуры об этом вызове еще ничего не известно. На самом деле, вызов some_routine (t) для t типа TRIANGLE или любого другого потомка класса POLYGON будет также корректен, таким образом, можно считать, что p представляет некоторый вид многоугольников - любой из их видов. Тогда вполне разумно, что к p применимы только компоненты класса POLYGON.

Таким образом, в случае, когда невозможно предсказать точный тип присоединяемого объекта, полиморфные сущности (такие как p) весьма полезны.

Может ли быть польза от неведения?

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

Если вы все еще испытываете неудобство от невозможности написать p.diagonal после присваивания p :=r (в случае (2)), то вы не одиноки. Это шокирует многих людей, когда они впервые сталкиваются с этими понятиями. Мы знаем, что p - это прямоугольник, почему же у нас нет доступа к его диагонали? По той причине, что это было бы бесполезно. После полиморфного присваивания, как показано на следующем фрагменте из предыдущего рисунка, один и тот же объект типа RECTANGLE имеет два имени: имя многоугольника p и прямоугольника r.

Рис. 14.7.  После полиморфного присваивания

В таком случае, поскольку известно, что объект O2 является прямоугольником и доступен через имя прямоугольника r, зачем пытаться использовать доступ к его диагонали посредством операции p.diagonal? Это не имеет смысла, так как можно просто написать r.diagonal, использовав официальное имя прямоугольника и сняв все сомнения в правомерности применения его операций. Использование имени многоугольника p, которое может с тем же успехом обозначать треугольник, ничего не дает и приводит к неопределенности.

Действительно, полиморфизм теряет информацию: когда в результате присваивания p :=r появляется возможность ссылаться на прямоугольник O2 через имя многоугольника p, то теряется нечто важное - возможность использовать специфические компоненты прямоугольника. В чем тогда польза? В данном случае - ни в чем. Как уже отмечалось, интерес возникает, когда заранее неизвестно, каков будет вид многоугольника p после выполнения команды if some_condition then p:= r else p := something_else ... или когда p является формальным аргументом процедуры и неизвестно, каков будет тип фактического аргумента. Но в этих случаях было бы некорректно и опасно применять к p что-либо кроме компонентов класса POLYGON.

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

0

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

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