Полиморфное создание
Когда хочется задать тип принудительно
В некоторых случаях нужно выполнить присваивание, не соответствующее структуре наследования, и допустить, что при этом в качестве результата не обязательно будет получен объект. Такого, обычно, не бывает, когда ОО-метод применяется к объектам, внутренним для некоторой программы. Но можно, например, поучить по сети объект с его объявленным типом, и поскольку нет возможности контролировать источник происхождения этого объекта, то объявления статических типов ничего не гарантируют и прежде, чем использовать объект, необходимо проверить его тип.
При получении коробки с надписью "Животное" вместо ожидаемой надписи "Собака", можно соблазниться и все же ее открыть, зная, что, если внутри будет не собака, то потеряется право на возврат посылки и, в зависимости от того, что из нее появится, можно лишиться даже возможности рассказать эту историю.В таких случаях требуется новый механизм - попытка присваивания , который позволит писать команду вида r ?= p (где ?= обозначает символ попытки присваивания, в отличие от := для обычного присваивания), означающую "выполнить присваивание, если тип объекта соответствует r , а иначе сделать r пустым". Но мы пока не готовы понять, как такая команда сочетается с ОО-методом, поэтому вернемся к этому вопросу в следующих лекциях. (А до того, считайте, что вы ничего об этом не читали).
Введение наследования и полиморфизма приводит к небольшому расширению механизма создания объектов, который позволит непосредственно создавать объекты типов-потомков.
Напомним, что команды создания (процедуры-конструкторы) имеют один из следующих видов:
create x
create x.make (...)
где вторая форма подразумевает и требует, чтобы базовый класс для типа T , приписанного x , содержал предложение creation , в котором make указана как одна из процедур-конструкторов. (Разумеется, процедура создания может иметь любое имя, - make рекомендуется по умолчанию). Результатом выполнения первой команды является создание нового объекта типа T , его инициализация значениями, заданными по умолчанию, и его присоединение к x . А при выполнении второй инструкции для создания и инициализации объекта будет вызываться make с заданными аргументами.
Предположим, что у T имеется собственный потомок U . Мы можем захотеть использовать x полиморфно и присоединить сразу к прямому экземпляру U , а не к экземпляру T . Возможное решение использует локальную сущность типа U .
some_routine (...) is
local
u_temp: U
do
...; create u_temp.make (...); x := u_temp; ...
end
Это работает, но чересчур громоздко, особенно в контексте многозначного выбора, когда захочется присоединить x к экземпляру одного из нескольких возможных типов наследников. Локальные сущности (u_temp в нашем примере) играют только временную роль, их объявления и присваивания загромождают текст программы. Поэтому нужны специальные варианты конструкторов:
create U x
create U x.make (...)
Результат должен быть тот же, что и у конструкторов create , приведенных выше, но создаваемый объект должен являться прямым экземпляром U , а не T . Этот вариант должен удовлетворять очевидному ограничению: тип U должен быть согласован с типом T , а во второй форме make должна быть определена как процедура создания в классе, базовом для U , и если этот класс имеет одну или несколько процедур создания, то применима лишь вторая форма. Заметим, что здесь не важно, имеет ли сам класс T процедуры создания, - все зависит только от U .
Типичное применение связано с созданием экземпляра одного из нескольких возможных типов:
f: FIGURE
...
"Вывести значки фигур"
if chosen_icon = rectangle_icon then
create RECTANGLE f
elseif chosen_icon = circle_icon then
create CIRCLE f
else
...
end
Этот новый вид конструкторов объектов приводит к введению понятия тип при создании , обозначающего тип создаваемого объекта в момент его создания конструктором:
Для формы с неявным типом create x ... тип при создании есть тип x .
Для формы с явным типом create U x ... тип при создании есть U .