Основы объектно-ориентированного программирования
Шрифт:
Поэтому остается единственная возможность - предусловие для Rescuer равно True. Это самое слабое предусловие, удовлетворяющее всем состояниям и означающее, что Rescuer должна работать во всех ситуациях.
Для ленивого создателя Rescuer это "плохая новость", - тот случай, когда "заказчик всегда прав"!
Что можно сказать о постусловии Rescuer? Напомню, эта часть предложения rescue ведет к отказу, но, прежде чем передать управление клиенту, необходимо восстановить стабильное состояние. Это означает необходимость восстановления инварианта класса.
Отсюда следует правило, в котором уже больше нет знаков вопросов:
Правило корректности для включающего отказ предложения rescue
4.
Похожие рассуждения дают правило для Retryr– части предложения rescue, включающей ветви, приводящие к инструкции retry:
Правило корректности для включающего повтор предложения rescue
5.
Четкое разделение ролей
Интересно сравнить формальные роли тела и предложения Rescuer:
Входное утверждение сильнее для Bodyr– в то время, когда Rescuer не накладывает никаких требований, перед началом выполнение тела программы (предложения do) должно выполняться предусловие и инвариант. Это упрощает работу Bodyr.
Выходное утверждение также сильнее для Bodyr– в то время, когда Rescuer обязана восстановить только инвариант класса, Bodyr обязана сыграть свою роль и обеспечить истинность выполнения постусловия. Это делает ее работу более трудной.
Эти правила отражают разделение ролей между предложением do и предложением rescue. Задача тела обеспечить выполнение контракта программы, не управляя непосредственно исключениями. Задача rescue– управлять обработкой исключениями, возвращая управление либо телу программы, либо вызывающей программе. Но в обязанности rescue не входит обеспечение контракта.
Когда нет предложения rescue
Формализовав роль предложения rescue, вернемся к рассмотрению ситуации, когда это предложение отсутствует в программе. Правило для этого случая было введено ранее, но с обязательством его уточнения. Ранее полагалось, что отсутствующее предложение rescue эквивалентно присутствию пустого предложения (rescue end). В свете наших формальных правил это не всегда является приемлемым решением. Правило (3) требует:
Если Rescuer является пустой инструкцией, а инвариант не тождественен True, то правило не выполняется.
Зададим точное правило. Класс Any является корневым классом - прародителем всех классов. В состав этого класса включена процедура default_rescue, наследуемая всеми классами - потомками Any:
Программа, не имеющая предложения rescue, рассматривается теперь как эквивалентная программе с предложением rescue в следующей форме:
Каждый класс может переопределить default_rescue, для выполнения специфических действий, гарантирующих восстановление инварианта класса, вместо эффекта пустого действия, заданного по умолчанию в GENERAL. Механизм переопределения компонент класса будет изучаться в последующих лекциях, посвященных наследованию.
Вы, конечно, помните, что одна из ролей процедуры создания состоит в производстве состояния, удовлетворяющего инварианту класса INV. Отсюда понятно, что во многих случаях переопределение default_rescue может основываться на использовании процедур создания.
Продвинутая обработка исключений
Чрезвычайно простой механизм, разработанный до сих пор, удовлетворяет большинству потребностей обработки исключений. Но некоторые приложения могут требовать более тонкой настройки:
[x]. Возможно, требуется определить природу последнего исключения, чтобы разными исключениями управлять по-разному.
[x]. Возможно, требуется запретить включение исключений для некоторых сигналов.
[x]. Возможно, вы захотите включать собственные исключения.
Можно было бы соответствующим образом расширить механизм, встроенный в язык, но это не кажется правильным подходом. Вот, по меньшей мере, три причины. Первая - свойства нужны только от случая к случаю, так что они будут загромождать язык. Вторая - все, что касается сигналов, может зависеть от платформы, а язык должен быть переносимым. Наконец, третья, - когда выбирается множество подобных свойств, никогда нет полной уверенности, что позже вам не захочется добавить новое свойство, что требовало бы модификации языка - не очень приятная перспектива.
В таких ситуациях следует обращаться не к языку, но к поддерживающим библиотекам. Мы введем библиотечный класс EXCEPTIONS, обеспечивающий необходимые возможности тонкой настройки. Классы, нуждающиеся в таких свойствах, будут наследниками EXCEPTIONS. Некоторые разработчики могут предпочесть отношение встраивания вместо наследования.
Запросы при работе с классом EXCEPTIONS
Класс EXCEPTIONS обеспечивает несколько запросов для получения требуемой информации о последнем исключении. Прежде всего, можно получить целочисленный код этого исключения: