Полиморфное создание


Когда хочется задать тип принудительно

 

В некоторых случаях нужно выполнить присваивание, не соответствующее структуре наследования, и допустить, что при этом в качестве результата не обязательно будет получен объект. Такого, обычно, не бывает, когда ОО-метод применяется к объектам, внутренним для некоторой программы. Но можно, например, поучить по сети объект с его объявленным типом, и поскольку нет возможности контролировать источник происхождения этого объекта, то объявления статических типов ничего не гарантируют и прежде, чем использовать объект, необходимо проверить его тип.

При получении коробки с надписью "Животное" вместо ожидаемой надписи "Собака", можно соблазниться и все же ее открыть, зная, что, если внутри будет не собака, то потеряется право на возврат посылки и, в зависимости от того, что из нее появится, можно лишиться даже возможности рассказать эту историю.В таких случаях требуется новый механизм - попытка присваивания , который позволит писать команду вида 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 .