Я напишу этот пример на Eiffel, чтобы его было проще понять.
my_code
-- Calls the `do_something' routine
do
set_table ("my_table")
do_something
end
do_something
-- Something to do
require
valid_table: is_valid_table (table_name)
do
sql_list := execute_sql_on_table (table_name)
ensure
has_result: sql_list.count > 0
end
sql_list: ARRAYED_LIST [STUFF]
table_name: STRING
set_table (a_name: STRING)
-- Set `table_name' to `a_name'
require
has_name: not a_name.is_empty
valid_table: is_valid_table (a_name)
do
table_name := a_name
ensure
table_name_set: table_name.same_string (a_name)
end
delete_table (a_name: STRING)
-- Delete `a_name' from the database.
require
valid_table: is_valid_table (a_name)
do
execute_sql ("DROP TABLE " + a_name)
ensure
table_gone: not is_valid_table (a_name)
end
- Функция `do_something '- это команда, в которой массив sql_list должен быть загружен с помощью STUFF из таблицы" my_table ".
- Предварительное условие для
do_something' makes it the responsibility of the client
my_code 'для предоставления table_name' before making the call to
do_something'.
- В свою очередь, договор о пост-условии обеспечения возлагает на него ответственность поставщика
do_something' fill the array
sql_list 'с экземплярами STUFF.
- Функция sql_list представляет собой запрос, возвращающий ссылочный указатель на массив STUFF.
- Аналогично, функция
table_name' is a query returning a reference pointer to a STRING, which is set with a "setter" command called
set_table '.
В этом случае «контракты» «Разработка по контракту» обеспечивают надлежащее разделение интересов и кто за что отвечает в этом небольшом фрагменте кода выше. Обратите внимание на явное отсутствие конструкций TRY-CATCH в коде. В этом случае ожидается, что источник данных будет иметь «my_table». Наличие контрактов означает, что программное обеспечение вызовет исключение, если контракт не будет выполнен. Сбой по требованию говорит о том, что вызывающий абонент неисправен, а сбой в проверке после условия указывает на функцию поставщика.
Наконец, этот код демонстрирует четкое разделение команд и запросов и применение гарантии качества, полученной при проектировании по контракту. Таким образом, на оригинальный вопрос можно ответить:
«Но как мы можем понять, что что-то в команде DoSomething () пошло не так? Как насчет команды sql (например, void Delete (Table))?
Хотя это может быть правдой, что вызов delete_table ("my_table") может быть внедрен в какого-то предка или может произойти в другом потоке, для этого и нужны контракты в do_something'. As long as those contracts stand guard over calls to
do_something ', процесс будет соответствующим образом обрабатываются. Внедренный вызов delete_table просто приведет к сбою контракта.
Все это предполагает, что НЕЛЬЗЯ БРОСАТЬ ТАБЛИЦУ на "my_table", и это трагически случайно. Тем не менее, если для «my_table» станет ОК для DROP TABLE, то для управления этим вариантом использования потребуется механизм повтора или другой «обработчик», и приведенный выше код работать не будет.