| обслуживает все виды, если наверняка известно, что, когда придет время еды, ваш кот получит кошачью еду, а пес - собачью. |
Переопределение и утверждения
Если клиент класса
Это противоречит духу переопределения. Переопределение должно изменять реализацию процедуры, а не ее семантику. К счастью, утверждения позволяют ограничить семантику процедур. Неформально, основное правило контроля за переопределением и динамическим связыванием можно сформулировать просто: предусловие и постусловие программы должны быть применимы к любому ее переопределению, и, как мы уже видели, инвариант класса автоматически должен распространяться на всех его потомков.
Точные правила будут приведены ниже. Но уже сейчас можно заметить, что переопределение не является произвольным: допускаются только переопределения, сохраняющие семантику. Это дело автора программы - выразить ее семантику достаточно точно, но оставить при этом свободу для будущих реализаторов.
О реализации динамического связывания
Может возникнуть опасение, что динамическое связывание - это дорогой механизм, требующий во время выполнения поиска по графу наследования и поэтому накладных расходов, растущих с увеличением глубины этого графа.
К счастью, это не так в случае хорошо спроектированного (и статически типизированного) ОО- языка. Более детально это будет обсуждаться в конце лекции, но мы можем уже сейчас успокоить себя тем, что последствия динамического связывания не будут существенными для эффективности при работе в подходящем окружении.
Отложенные компоненты и классы
Полиморфизм и динамическое связывание означают, что в процессе проектирования ПО можно рассчитывать на абстракции и быть уверенными в том, что при выполнении будет выбрана подходящая реализация. Но перед выполнением все должно быть полностью реализовано.
Однако полная реализация не всегда нужна. Частично реализованные или не реализованные абстрактные элементы ПО помогают при решении многих задач: анализе проблемы и проектировании архитектуры системы (в этом случае можно их сохранить в заключительном продукте, чтобы запомнить ход анализа и проектирования), при фиксации соглашений между реализаторами, при описании промежуточных точек в классификации.
Отложенные компоненты и классы обеспечивают необходимый механизм абстракции.
Движения произвольных фигур
Чтобы понять необходимость в отложенных процедурах и классах, снова рассмотрим иерархию фигур
Рис. 14.8. Снова иерархия FIGURE
Наиболее общим понятием здесь является
transform (f: FIGURE) is
-- Применить специфическое преобразование к f.
do
f.rotate (...)
f.translate (...)
end
с соответствующими значениями опущенных аргументов. Тогда все следующие вызовы корректны:
transform (r) -- для r: RECTANGLE
transform (c) -- для c: CIRCLE
transform (figarray.item (i)) -- для массива фигур: ARRAY [POLYGON]
Иными словами, требуется применить преобразования
Это действительно работает и является типичным примером элегантного стиля, ставшего возможным благодаря полиморфизму и динамическому связыванию, стиля, основанного на принципе Единственного выбора. Требуется только переопределить
Но переопределять-то нечего! Класс
Таким образом, мы имеем ситуацию, в которой процедура
