Архитектура программы
Следуя традиционным рекомендациям декомпозиции сверху вниз, выберем "вершину" - главную функцию нашей системы. Это должна быть, очевидно, программа execute_session, описывающая выполнение полной интерактивной сессии.
Рис. 2.3. Функциональная декомпозиция сверху-вниз
Непосредственно ниже (уровень 2) найдем операции, связанные с состояниями: определение начального и конечного состояний, структуру переходов и функцию execute_state, описывающую действия, выполняемые в каждом состоянии. На нижнем уровне 1 найдем операции, определяющие execute_state: отображение панели на экране и другие. Заметьте, что и это решение, также как и ОО-решение, описываемое чуть позже, отражает "реальный мир", в данном случае включающий состояния и элементарные операции данного мира. В этом примере и во многих других не в реальности мира состоит важная разница между ОО-подходом и другими решениями, а в том, как мы моделируем этот мир.
При написании программы execute_session попытаемся сделать наше приложение максимально независимым. (Наша нотация выбрана в соответствии с примером. Цикл repeat until заимствован из Pascal.)
execute_session is -- Выполняет полную сессию интерактивной системы local state, next: INTEGER do state := initial repeat execute_state (state, >next) -- Процедура execute_state обновляет значение next state := transition (state, next) until is_final (state) end endЭто типичный алгоритм обхода диаграммы переходов. (Те, кто писал лексический анализатор, узнают образец.) На каждом этапе мы находимся в состоянии state, вначале устанавливаемом в initial; процесс завершается, когда состояние удовлетворяет is_final. Для состояний, не являющихся заключительными, вызывается execute_state, принимающее текущее состояние и возвращающее в аргументе next выбор перехода, сделанный пользователем. Функция transition определяет следующее состояние.
Техника, используемая в процедуре execute_state, изменяющая значение одного из своих аргументов, никогда не подходит для хорошего ОО-проекта, но здесь она вполне приемлема.
Для того чтобы сделать ее явной, используется "флажок" для "out" аргумента - next со стрелкой.
Для завершения проекта следует определить процедуру execute_state, описывающую действия, выполняемые в каждом состоянии. Ее тело реализует содержимое блока начальной goto-версии.
execute_state (in s: INTEGER; out c: INTEGER) is -- Выполнить действия, связанные с состоянием s, -- возвращая в c выбор состояния, сделанный пользователем local a: ANSWER; ok: BOOLEAN do repeat display (s) read (s, a) ok := correct (s, a) if not ok then message (s, a) end until ok end process (s, a) c := next_choice (a) endЗдесь вызываются программы уровня 1 со следующими ролями:
- display (s) выводит на экран панель, связанную с состоянием s;
- read (s, a) читает в a ответы пользователя, введенные в окнах панели состояния s;
- correct (s, a) возвращает true, если и только если a является приемлемым ответом; если да, то process (s, a) обрабатывает ответ a, например, обновляя базу данных или отображая некоторую информацию, если нет, то message (s, a) выводит соответствующее сообщение об ошибке.
Для получения работающего приложения необходимо задать реализации программ уровня 1: display, read, correct, message и process.