my_last_book: BOOK
... Сравните с := в первой попытке
my_last_book ?= retrieved (my_book_file)
if my_last_book /= Void then
... 'Обычные операции над my_last_book' ...
else
... 'Полученное не соответствует ожиданию' ...
end
Правильное использование попытки присваивания
Необходимость попытки присваивания обусловлена, как правило, тем, что на статически объявленный тип сущности положиться нельзя, а опознать тип фактически адресуемого объекта необходимо 'на лету'. Например, при работе с полиморфными структурами данных и получении объектов из третьих рук.
Заметьте, как тщательно был спроектирован механизм, дающий разработчикам шанс забыть об устаревшем стиле разбора вариантов (case-by-case). Если вы действительно хотите перехитрить динамическое связывание и отдельно проверять каждый вариант типа, вы можете это сделать, хотя вам и придется немало потрудиться. Так, вместо обычного
display (f: FIGURE) is
-- Отобразить f, используя алгоритм,
-- адаптируемый к истинной природе объекта.
local
r: RECTANGLE; t: TRIANGLE; p: POLYGON; s: SQUARE
sg: SEGMENT; e: ELLIPSE; c: CIRCLE;?
do
r ?= f; if r /= Void then 'Использовать алгоритм вывода прямоугольника' end
t ?= f; if t /= Void then 'Использовать алгоритм вывода треугольника' end
c ?= f; if c /= Void then 'Использовать алгоритм вывода окружности' end
... и т.д. ...
end
На практике такая схема даже хуже, чем кажется, так как структура наследования имеет несколько уровней, а значит, усложнения управляющих конструкций не избежать.
Из-за трудностей написания таких закрученных конструкций попытки присваивания новичкам вряд ли придет в голову использовать их вместо привычной ОО-схемы. Однако и опытные специалисты должны помнить о возможности неправильного использования конструкции.
Типизация и повторное объявление
Повторное объявление компонентов не требует сохранения сигнатуры. Пока оно виделось нам как замена одного алгоритма другим или - для отложенного компонента - запись алгоритма, соответствующего ранее заданной спецификации.
Но, воплощая идею о том, что класс способен предложить более специализированную версию элемента, описанного его предком, мы вынуждены иногда изменять типы данных. Приведем два характерных примера.
Устройства и принтеры
Вот простой пример переопределения типа. Рассмотрим понятие устройства, включив предположение о том, что для любого устройства есть альтернатива, так что устройство можно заменить, если оно по каким-либо причинам недоступно:
class DEVICE feature
alternate: DEVICE
set_alternate (a: DEVICE) is
-- Пусть a - альтернативное устройство.
do
alternate := a
end
... Прочие компоненты ...
end
Принтер является устройством, так что использование наследования оправдано. Но альтернативой принтера может быть только принтер, но не дисковод для компакт-дисков или сетевая карта, - поэтому мы должны переопределить тип:
Рис. 16.6. Устройства и принтеры
class PRINTER inherit