Проектирование по контракту

Проектирование по контракту (Design by Contract) - это метод про­ектирования, являющийся центральным свойством языка Eiffel. И метод, и язык разработаны Бертраном Мейером [33]. Однако про­ектирование по контракту не является привилегией только языка Eiffel, этот метод можно применять и в любом другом языке про­граммирования.

Главной идеей проектирования по контракту является понятие утверждения. Утверждение (assertion) - это булево высказывание, которое никогда не должно принимать ложное значение и поэтому может быть ложным только в результате ошибки. Обычно утверж­дение проверяется только во время отладки и не проверяется в ре­жиме выполнения. Действительно при выполнении программы никогда не следует предполагать, что утверждение проверяется.

В методе проектирования по контракту определены утверждения трех типов: предусловия, постусловия и инварианты. Предусловия и постусловия применяются к операциям. Постусловие - это вы­сказывание относительно того, как будет выглядеть окружающий мир после выполнения операции. Например, если мы определяем для числа операцию «извлечь квадратный корень», постусловие может принимать форму input = result * result, где result является выходом, a input - исходное значение числа. Постусловие — это хо­роший способ выразить, что должно быть сделано, не говоря при этом, как это сделать. Другими словами, постусловия позволяют отделить интерфейс от реализации.

Предусловие - это высказывание относительно того, как должен выглядеть окружающий мир до выполнения операции. Для опера­ции «извлечь квадратный корень» можно определить предусловие input >= 0. Такое предусловие утверждает, что применение опера­ции «извлечь квадратный корень» для отрицательного числа явля­ется ошибочным и последствия такого применения не определены.

На первый взгляд эта идея кажется неудачной, поскольку нам придется выполнить некоторые дополнительные проверки, чтобы убедиться в корректности выполнения операции «извлечь квад­ратный корень». При этом возникает важный вопрос: на кого ля­жет ответственность за выполнение этой проверки.

Предусловие явным образом устанавливает, что за подобную про­верку отвечает вызывающий объект. Без такого явного указания обязанностей мы можем получить либо недостаточный уровень проверки (когда каждая из сторон предполагает, что ответствен­ность несет другая сторона), либо чрезмерную проверку (когда она будет выполняться обеими сторонами). Излишняя проверка тоже плоха, поскольку это влечет за собой дублирование кода проверки, что, в свою очередь, может существенно увеличить сложность про-


граммы. Явное определение ответственности помогает снизить сложность кода. Опасность того, что вызывающий объект забудет выполнить проверку, уменьшается тем обстоятельством, что утвер­ждение обычно проверяется во время отладки и тестирования.

Исходя из этих определений предусловия и постусловия, мы мо­жем дать строгое определение термина исключение. Исключение возникает, когда предусловие операции выполнено, но операция не может возвратить значение в соответствии с указанным пост­условием.

Инвариант представляет собой утверждение относительно класса. Например, класс Account (Счет) может иметь инвариант, который утверждает, что balance == sum(entries.amount()). Инвариант дол­жен быть «всегда» истинным для всех экземпляров класса. В дан­ном случае «всегда» означает «всякий раз, когда объект доступен для выполнения над ним операции».

По существу это означает, что инвариант дополняет предусловия и постусловия, связанные со всеми открытыми операциями данного класса. Значение инварианта может оказаться ложным во время выполнения некоторого метода, однако оно должно снова стать ис­тинным к моменту взаимодействия с любым другим объектом.

Утверждения могут играть уникальную роль в определении под­классов. Одна из опасностей наследования состоит в том, что опе­рации подкласса можно переопределить так, что они станут не со­вместимыми с операциями суперкласса. Утверждения уменьшают вероятность этого. Инварианты и постусловия класса должны при­меняться ко всем подклассам. Подклассы могут усилить эти утвер­ждения, но не могут их ослабить. С другой стороны, предусловия нельзя усилить, но можно ослабить.

На первый взгляд все это кажется излишним, однако имеет весьма важное значение для обеспечения динамического связывания. В со­ответствии с принципом замещения необходимо всегда иметь воз­можность обратиться к объекту подкласса так, как если бы он был экземпляром суперкласса. Если подкласс усилил свое предусло­вие, то операция суперкласса, примененная к подклассу, может за­вершиться аварийно.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: