
2.5 Пример Класс стек
Предусловия выражают ограничения, выполнение которых необходимо для корректной работы функции.
Здесь:
• put не может быть вызвана, если стек заполнен;
• remove и item не могут быть применены к пустому стеку.
Предусловия применяются ко всем вызовам программы, как внутри класса, так и у клиента. Корректная система никогда не вызовет программу в состоянии, в котором не выполняется ее предусловие.
Постусловие выражает свойство состояния, завершающего выполнение программы. Здесь:
• После завершения put стек не может быть пуст; на его вершине находится только что втолкнутый элемент, число его элементов увеличилось на единицу.
• После remove стек не может быть полон, число его элементов на единицу уменьшилось.
Постусловие в программе выражает гарантию, представленную создателем программы, что выполнение программы завершается и приводит к состоянию с заданными свойствами, в предположении, что программа была запущена в состоянии, удовлетворяющем предусловию.
2.6 Контракты и надежность по
Предусловие и постусловие программы определяют контракт со всеми ее клиентами.
Права и обязательства
Связывая с программой r предложения require pre и ensure post, класс говорит своим клиентам: "Если вы обещаете вызвать r в состоянии, удовлетворяющем pre, то я обещаю в заключительном состоянии выполнить post " .
В отношениях между людьми и компаниями контракт - это письменный документ, фиксирующий отношения. Удивительно, что в программной индустрии, где точность так важна и двусмысленность так рискованна, эта идея так долго не появлялась. Любой хороший контракт устанавливает для обоих участников как обязательства, так и приобретаемую выгоду; обычно обязательства одного оборачиваются выгодой для другого участника, и это взаимно. Все это верно и для контрактов между классами.
• Предусловие связывает клиента: определяются условия, при которых вызов программы клиентом легитимен. Обязательства клиента приносят пользу поставщику.
• Постусловие связывает класс: программа обязана обеспечить условия по ее завершению. Здесь польза клиента оборачивается обязательствами поставщика класса.
Контракт - один из главных вкладов в надежность ПО.
Правило контракта говорит, что предусловие дает преимущество поставщику, если клиентская часть контракта не выполняется, то класс перестает быть связан постусловием. В этом случае программа может делать все что угодно, например зациклиться, не нарушая при этом контракт. Это тот самый случай, когда "заказчик виноват".
Первое преимущество от такого соглашения в том, что стиль программирования существенно упрощается. Разработчик класса при написании тела программы смело может предполагать, что все ограничения, заданные предусловием, выполняются; ему нет нужды проверять их в теле программы. Так для функции, вычисляющей квадратный корень:
def sqrt (x) :
assert x >= 0
можно смело применять алгоритм, не учитывающий случай отрицательного x, поскольку это предусмотрено предусловием, и ответственность за его выполнение несут клиенты программы. С первого взгляда это может показаться опасным, но читайте дальше. Фактически метод Проектирования по Контракту идет дальше. Предположим, что мы написали в предложении do предыдущей программы следующий текст:
if x < 0 :
print " Обработать ошибку как-нибудь "
else :
print "Выполнить нормальное вычисление квадратного корня"
Принцип Нет-Избыточности
Ни при каких обстоятельствах в теле программы не должно проверяться ее предусловие. Это правило противоречит тому, чему учат во многих учебниках по программирования, где необходимость проверок часто выступает под знаменами "защитного программирования" (defensive programming) .
Его идея в том, что для получения надежного ПО каждая программа должна защищать себя настолько, насколько это возможно. Лучше больше проверок, чем недостаточно.
Проектирование по контракту утверждает противное: избыточные проверки могут нанести вред.
Возьмем, например, программу sqrt, включившую проверку x<0, хотя ее клиенты были проинструктированы о необходимости обеспечения x>=0. Что в этом плохого? С микроскопической точки зрения кажется, что включение проверки делает программу более устойчивой.
Но мир системы не ограничивается одной программой - он содержит множество программ в множестве классов. Для получения надежной системы необходимо перейти к макроскопическому видению проблемы, обобщающему всю архитектуру.
С этой глобальной точки зрения простота становится критическим фактором. Сложность – главный враг качества.
Добавляя избыточные проверки, добавляете
больше кода. Больше кода - больше сложности, отсюда и больше источников условий, приводящих к тому, что все пойдет не так, это приведет к дальнейшему разрастанию кода и так до бесконечности. Если пойти по этой дороге, то определенно можно сказать одно - мы никогда не достигнем надежности. Чем больше пишем, тем больше придется писать.
Проектирование по Контракту приглашает идентифицировать согласованные условия, необходимые для правильного функционирования каждого контракта в кооперации клиенты - поставщики. Метод вынуждает для каждого соглашения установить, кто несет ответственность - клиент или поставщик. Ответ может быть разный, частично он определяется стилем проектирования. Но когда решение принято, нужно его придерживаться. Если требования корректности появляются в предусловии, определяя тем самым ответственность клиента, то в программе не должно быть соответствующих проверок. Требования, не указанные в предусловии, должны проверяться и выполняться в программе.
Еще одним недостатком защитного программирования является его стоимость. Потеря производительности - наказание за избыточные проверки. Иногда этого вполне достаточная причина для отказа от защитного программирования.
Не говоря уже о потере производительности, принципиальной причиной отказа от защитного программирования является наша цель - получение максимальной надежности. Для систем сколь либо существенных размеров недостаточно обеспечение качества отдельных элементов, - более важно гарантировать, что для каждого взаимодействия двух элементов задан явный список взаимных обязательств и преимуществ - контракт. В заключение сформулируем парадокс Дзен-стиля: меньше проверок - больше надежности.