Механизм решения
Механизм решения
И снова запись механизма решения напрямую вытекает из анализа поставленной проблемы. Введем новую форму присваивания, назвав ее попыткой присваивания (assignment attempt):
target ?= source
Знак вопроса указывает на предварительный характер операции. Пусть сущность target имеет тип T, тогда попытка присваивания дает следующий результат:
[x]. если source ссылается на объект совместимого с T типа, присоединить target к объекту так, как это делает обычное присваивание;
[x]. иначе (если source равно void или ссылается на объект несовместимого типа) приписать target значение void.
На эту инструкцию не действуют никакие ограничения типов, кроме одного: тип target (T) должен быть ссылочным.
Новое средство быстро и элегантно решает поставленные проблемы и, прежде всего, дает возможность обращаться к объектам полиморфной структуры с учетом их типа:
maxdiag (figlist: LIST [FIGURE]): REAL is
-- Максимальная длина диагонали прямоугольника в списке;
-- если прямоугольников нет, то -1.
require
list_exists: figlist /= Void
local
r: RECTANGLE
do
from
figlist.start; Result := -1.0
until
figlist.after
loop
r ?= figlist.item
if r /= Void then
Result := Result.max (r.diagonal)
end
figlist.forth
end
end
Здесь применяются обычные итерационные механизмы работы с последовательными структурами данных (лекция 5 курса "Основы объектно-ориентированного проектирования"). Компонент start служит для перехода к первому элементу (если он есть), after - для выяснения того, имеются ли еще не пройденные элементы, forth - для перехода на одну позицию, item (определенный, если not after) - для выборки текущего элемента.
В попытке присваивания используется локальная сущность r типа RECTANGLE. Успех присваивания проверяется сравнением значения r с Void. Если r не Void, то r прямоугольник и можно обратиться к r.diagonal. Эта схема проверки вполне типична.
Заметим, что мы никогда не нарушаем правило Вызова Компонентов: обращения к r.diagonal защищены дважды: статически - компилятором, проверяющим, является ли diagonal компонентом класса RECTANGLE, и динамически - нашей гарантией того, что r не Void, а имеет присоединенный объект.
Обращение к элементу списка - потомку класса RECTANGLE, например SQUARE (квадрат), связывает r с объектом, и его диагональ будет участвовать в вычислениях.
Пример с универсальной функцией чтения объектов retrieval выглядит так:
my_last_book: BOOK
... Сравните с := в первой попытке
my_last_book ?= retrieved (my_book_file)
if my_last_book /= Void then
... "Обычные операции над my_last_book" ...
else
... "Полученное не соответствует ожиданию" ...
end