Проектирование предусловий: толерантное или требовательное?
Проектирование предусловий: толерантное или требовательное?
Центральная идея Проектирования по контракту выражена в принципе Нет_Избыточности, суть которого в том, что за выполнение условия, необходимого для правильного функционирования программы, должен нести ответственность только один из партнеров контракта.
Какой же? В каждом случае есть две возможности.
[x]. Ответственность возлагается на клиента. В этом случае условие становится частью предусловия программы.
[x]. За все отвечает поставщик. Тогда условие появится в программе, являясь частью разбора возможных ситуаций.
Первую ситуацию назовем требовательной (demanding), вторую - толерантной (tolerant). Класс STACK2 иллюстрирует требовательный стиль, толерантная версия, не содержащая предусловий, может выглядеть так:
remove is
-- Удалить элемент вершины стека
do
if empty then
print ("Ошибка: попытка удаления элемента из пустого стека")
else
count := count - 1
end
end
Проводя аналогию с контрактами между людьми, требовательный стиль характерен для опытного поставщика, имеющего хорошо поставленное дело, и требующего от своих клиентов, чтобы они до обращения к нему выполнили всю необходимую предварительную работу. Толерантный стиль вызывает образ вновь образованной фирмы, старающейся завоевать своих клиентов, и выставляющей с этой целью рекламный плакат:
Рис. 11.3. Реклама толерантного стиля
Какой же из стилей лучше? С первого взгляда может казаться, что толерантный стиль лучше, как с позиций повторного использования, так и для повышения надежности. В требовательном стиле на всех клиентов одного поставщика ложится ответственность за выполнение ряда условий; при повторном использовании число таких клиентов только возрастает. Так не эффективнее и надежнее было бы потребовать, чтобы эту ответственность брал на себя поставщик, освобождая клиентов от обязательств?
Исследуем эту проблему чуть глубже. Условие корректности описывает требования, необходимые для успешной работы программы. Толерантная версия программы remove является хорошим примером, демонстрирующим слабости этого стиля. Действительно, что может сделать бедная, занимающаяся выталкиванием программа, когда стек пуст? Она делает храбрую попытку выдать явно неинформативное сообщение об ошибке, но на большее она не способна - ей недоступен контекст клиента, она не способна определить, что нужно делать, когда стек пуст. Только клиент - модуль, использующий стек для своих целей, например, модуль разбора текста в компиляторе, - обладает достаточной информацией для принятия нужного решения. Является ли это нормальным, хотя и бесполезным запросом, который следует просто проигнорировать. Если это ошибка, как следует ее обработать: выбросить ли исключение, попытаться скорректировать ситуацию, или, в крайнем случае, выдать информативное для пользователя сообщение об ошибке.
Обсуждая пример с квадратным корнем, приводился такой вариант программы:
if x < 0 then
"Обработайте ошибку как-нибудь"
else
"Выполнить нормальное вычисление квадратного корня"
end
Ключевое слово здесь "как-нибудь". Предложение then скорее заклинание, чем программный код: нет разумной, общецелевой техники обработки случая x<0. Только автор клиента может знать, что значит этот случай - ошибка в ПО, возможность замены нулевым значением, причина для возбуждения исключения...
Ситуация, в которую попала толерантная версия remove, напоминает почтальона, который должен доставить письмо, не содержащее ни адреса получателя, ни адреса отправителя, - немногое может сделать такой почтальон.
Соответствуя духу Проектирования по контракту, требовательный подход к проектированию предусловий не пытается создавать программы, выполняющие все для своих клиентов. Более того, его суть в том, что каждая программа выполняет только хорошо определенную часть работы, но делает ее хорошо (корректно, эффективно, способную повторно использоваться многими клиентами). Такая программа четко классифицирует случаи, с которыми она не может справиться. Автор программы не должен пытаться быть умнее своих клиентов, если он не уверен, что должна делать программа в некоторой критической ситуации, он должен исключить этот случай из программы и явно включить его в предусловие. Эта позиция является следствием основной темы, проходящей через всю книгу, - создание программных систем как множества модулей, занятых своим делом.
Есть сходство в данном обсуждении и обсуждении использования частичных функций в математических моделях, рассматриваемое в лекции про АТД. Там говорилось, что целесообразнее использовать частичные функции, чем делать функцию всюду определенной, введением специального неопределенного значения - ?integer. Эти две идеи близки, Проектирование по контракту является частью применения к программным конструкциям концепции частичных функций, замечательно гибкого и мощного аппарата формальных спецификаций.Предупреждение: требовательный подход применим при условии, что предусловия являются разумными и обоснованными. В противном случае, работа была бы достаточно простой, достаточно для каждого модуля написать предусловие False, и любая программа была бы корректной. Дадим более точную характеристику "обоснованности" предусловия:
Принцип обоснованности предусловия
Каждое предусловие программы при требовательном стиле проектирования должно удовлетворять следующим требованиям:
1 Предусловие появляется в официальной документации, поставляемой авторам клиентских модулей.
2 Предусловие формулируется только в терминах спецификации, что делает возможным его вычисление.
Первое требование поддерживается понятием краткой формы, изучаемой позднее в этой лекции. Второе требование исключает появление ограничений, определяемых реализацией поставщика программы. Например, для программы, занимающейся выталкиванием элементов из стека, предусловие not empty является требованием, проверяемым в терминах спецификации, и вытекающим из очевидного факта - из пустого стека ничего нельзя вытолкнуть. При вычислении квадратного корня предусловие x>0 отражает известный математический факт, - отрицательные числа не имеют вещественных квадратных корней.
Некоторые ограничения могут навязываться реализацией. Например, в программе put из класса STACK2 присутствие в качестве предусловия require not full связано с реализацией стека на основе массива. Но это не является нарушением принципа, поскольку класс STACK2 в полном соответствии с его спецификацией определяет стеки ограниченной емкости, что отражено, например, в предложении indexing этого класса. АТД, служащий в роли спецификации этого класса, не задает наиболее общий вид стеков, но является понятием стека ограниченной емкости.
Обычно следует избегать структур ограниченной емкости; даже в случае массивов можно строить стек на динамических массивах, изменяющих размерность при необходимости. В Базовой библиотеке представлен общий класс, описывающий стеки11.1), отличающийся от класса STACK2 тем, что в нем не используется понятие емкости; стек по умолчанию перестраивается, когда текущей емкости недостаточно для хранения очередного поступающего элемента.Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Проектирование здания
Проектирование здания Рассмотрим простой пример, в котором создадим стандартный проект с помощью специального мастера House Builder Wizard (Мастер строительства дома).Откройте программу 3D Home Architect Design Suite Deluxe и вызовите мастер, щелкнув кнопкой мыши на запускающем программу
8.2. Проектирование
8.2. Проектирование Архитектурный каркас Каждая программная система должна иметь простую и в то же время всеобъемлющую организационную философию. Система мониторинга погоды не является в этом смысле исключением. На следующем этапе нашей работы мы должны четко
9.2. Проектирование
9.2. Проектирование Тактические вопросы В соответствии с законом разработки программ Коггинса "прагматизм всегда должен быть предпочтительней элегантности, ведь Природу все равно ничем не удивить". Следствие: проектирование никогда не будет полностью независимым от
10.2. Проектирование
10.2. Проектирование Формулируя подходы к архитектуре системы складского учета, мы должны помнить о трех моментах организационного характера: разделение функций между клиентской и серверной частью, механизм управления транзакциями, стратегия реализации клиентской
11.2. Проектирование
11.2. Проектирование Архитектура информационной доски Теперь у нас есть все, чтобы приступить к решению поставленной задачи с использованием метафоры информационной доски. Это классический пример повторного использования "в большом": мы повторно применяем испытанный
12.2. Проектирование
12.2. Проектирование Как уже отмечалось в главе 6, создание архитектуры подразумевает выявление основной структуры классов и спецификацию общих взаимодействий, которые оживляют классы. Сконцентрировав внимание прежде всего на этих механизмах, мы с самого начала выявляем
Правило 19: Рассматривайте проектирование класса как проектирование типа
Правило 19: Рассматривайте проектирование класса как проектирование типа В C++, как и в других объектно-ориентированных языках программирования, при определении нового класса определяется новый тип. Потому большую часть времени вы как разработчик C++ будете тратить на
Проектирование дверей
Проектирование дверей Для создания дверей нужно нажать кнопку палитры инструментов Door (Дверь) – на информационной палитре появятся элементы управления настройкой параметров двери (рис. 6.4). Рис. 6.4. Элементы управления настройкой параметров
Проектирование балок
Проектирование балок Следующий инструмент проектирования строительных конструкций – балки. Для активизации этого инструмента щелкните на кнопке Beam (Балка) раздела Design (Проектирование) палитры ToolBox (Палитра инструментов). Основные параметры балок отобразятся на
Проектирование лестниц
Проектирование лестниц Переходим к одному из самых интересных объектов библиотеки ArchiCAD – лестницам. Интересен он не только потому, что существует достаточно большое количество видов этого объекта и выбор лестницы с настройкой ее параметров превращается в
Интерфейсы и проектирование ПО
Интерфейсы и проектирование ПО Архитектура системы должна основываться на содержании, а не на форме. Но проектирование сверху вниз стремится использовать в качестве основы для структуры самый поверхностный аспект системы - ее внешний интерфейс. Такой упор на внешний
У8.3 Проектирование нотации
У8.3 Проектирование нотации Предположим, вы часто используете сравнение в форме x.is_equal (y), и хотите упростить нотацию, используя преимущества инфиксной записи (применимой здесь, поскольку наша функция имеет один аргумент). Для инфиксного компонента используйте некоторый
12.9. ТЕХНИЧЕСКОЕ ПРОЕКТИРОВАНИЕ
12.9. ТЕХНИЧЕСКОЕ ПРОЕКТИРОВАНИЕ Техническое проектирование — это мост между функциональной спецификацией и фактической стадией кодирования. Часто команда разработчиков пытается сократить и объединить стадию разработки функциональной спецификации и техническое
Проектирование
Проектирование Ключевым аспектом развертывания PKI является выбор архитектуры и проектирование. PKI допускает гибкость проектирования независимо от выбранной технологии. Этап проектирования занимает длительное время, так как на этом этапе должна быть сформирована