|
|
"Design By Contract". Краткий обзор.
"Design By Contract". Краткий обзор.
В кратце попробую своими словами изложить суть "Design By Contract", а также приведу примеры как можно использовать DBC с языками где нет прямой поддержки и пару слов о смежных технологиях :)
Предположим, что у нас есть сервер(тот кто предоставляет некоторый сервис) и есть клиент(тот кому необходимо воспользоваться этим сервисом). Суть идеологии в том, что клиент и сервер заключают между собой контракт : клиент должен выполнить условия которые требует сервер для того чтобы он(сервер) мог предоставить требуемый сервис, сервер должен гарантировать что он выполнил именно ту операцию которую заказывал клиент, а также сервер должен гарантировать что все время работы находиться во "вменяемом" состоянии. Таким образом, DBC позволяет задать preconditions - условия которые должен выполнить клиент, postconditions - условия которые должен выполнить сервер и invariant - условия нормальной работы сервера.
Приведу пример кода, который демонстрирует использование DBC в Eiffel. Метод put, помещает в некоторый контейнер элемент x по ключу key.
put (x: ELEMENT; key: STRING) is
-- Insert x so that it will be retrievable through key.
require
not key.empty
deferred
ensure
has (x)
item (key) = x
count = old count + 1
end
---
invariant
count <= capacity
--- |
Здесь мы видим, что от клиента требуется чтобы он предоставил не пустой ключ(содержимое require), от сервера требуется чтобы после выполнения операции он действительно содержал элемент x, чтобы этот элемент x можно было найти по ключу key и что размер контейнера увеличился на один элемент(содержимое ensure) и то что в течении всего времени работы сервера count не превышал capacity(invariant). Содержимое выражения require проверяется перед выполнением тела метода put, содержимое выражения ensure выполняется после выполнения тела метода put, invariant проверяется до и после выполнения тела метода put. deffered означает что метод абстрактный(если использовать термин из С++). Таким образом мы можем на стадии создания интерфейсов задавать семантику метода. Причем со стороны языка поддерживается наследование pre\post-conditions и invariants, задание pre\post-conditions и invariants для абстрактных интерфейсов.
При программировании на С++ можно использовать DBC с помощью define и функции assert ( ); К сожалению нужно многое делать руками, и все равно всех преимуществ получить не удается. Для задания pre\post-conditions можно пользоваться функцией assert, для задания invariant нужно написать такой define:
#ifndef NDEBUG
#define invariant() (this->Invariant ())
#else
#define invariant()
#endif |
в теле класса написать иплементацию :
class class_for_example
{
public:
put (x: ELEMENT; key: STRING)
{
// Insert x so that it will be retrievable through key.
assert ( ! key.empty )
invariant ( );
int old_count = count;
/* any code */
invariant ( );
assert ( has (x) )
assert ( item (key) == x )
assert ( count = old_count + 1 )
}
protected :
void Invariant ( void ) const
{
assert ( count <= capacity );
}
} |
Как использовать DBC с другими языками программирования я рассматривать не буду. Это можно либо сделать по аналогии, либо почитать литературу посвященную этому вопросу ...
Еще хочу отметить отличие DBC и метода который предлагает eXtreme Programming(это, правда, лично мое впечатление, тем более что я не очень углублялся в то что предлагает XP). Остановиться на этом вопросе я решил, для того, чтобы не создавалось впечатление, что эти две методологии примерно одно и то же. В XP предлагается сначала писать тесты для интерфейсов и уже потом писать сами интерфейсы. В случае если эти тесты затем выполняются в течении всего процесса разработки - в некотором смысле - их можно рассматривать как аналог контракта из DBC. Мне не очень было понятно как можно писать тесты для интерфейсов которых еще нет :. Потом, если контракт находится там же где и интерфейс - для того чтобы понять что делает этот интерфейс нужно взглянуть только на интерфейс, в случае с XP нужно смотреть еще и тесты. То есть информация получается разбросана. И в этом смысле, например, неясно как должны поставляться библиотеки - со своим набором тестов ?. Также, с точки зрения трудоемкости, на мой взгляд, тяжело поддерживать в синхронном состоянии вещи которые находятся в разных местах. Еще, тесты как правило содержат гораздо больше проверок, чем это необходимо в DBC, на различные специальные случаи, на ошибки которые уже были ну и т.д. то есть рассматривать их как контракт было бы неверно с этой точки зрения. Таким образом, DBC и тесты - это разные вещи - причем и одно и другое необходимо, но на разных этапах, DBC при проектировании интерфейсов, тесты при тестировании(DBC, к слову, может помочь при составлении тестов).
Ссылки, где можно узнать больше:
- Во-первых рекоммендую посмотреть ссылки уже приведенные в форуме : Их предоставил W :
http://www.eiffel.com/doc/manuals/technology/contract/page.html
http://www.elj.com/eiffel/dbc/
http://www.inferdata.com/~richard/extras/dbcextracts/
- . "Object-Oriented Software Construction, Second Edition" Bertrand Meyer.
http://www.eiffel.com/doc/oosc/
- http://www.irisa.fr/prive/jezequel/DesignPatterns/#Preface
- http://www.objectmentor.com/publications/lsp.pdf
- http://www.reliable-systems.com/tools/iContract/iContract.htm
- http://www.aw.com/cseng/titles/0-201-89542-0/techniques/designByContract.htm
- http://www.eiffel.com/doc/manuals/technology/contract/ariane/page.html
Alexey Lapshin(dotSITE virtual team)
|