Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4086 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 2:

Образец проектирования: многопанельные интерактивные системы

< Лекция 1 || Лекция 2: 123456 || Лекция 3 >

Функциональное решение: проектирование сверху вниз

Повторяя на этом частном примере эволюцию, пройденную программированием в целом, перейдем от низкоуровневого подхода goto к иерархической структуре, создаваемой при проектировании сверху вниз. Это решение принадлежит общему стилю, известному как "структурное", хотя этот термин следует использовать с осторожностью.

ОО-решение является, конечно же, структурным, соответствуя духу "структурного программирования", как оно изначально было введено Э. Дейкстрой.

Функция переходов

Первым шагом на пути улучшения нашего решения будет придание центральной роли алгоритму обхода в структуре ПО. Диаграмма переходов - это всего лишь одно из свойств нашей системы, и нет никаких оснований, чтобы она правила над всеми частями системы. Ее отделение от остального алгоритма позволит, по крайней мере, избавиться от goto. Можно ожидать и большей общности, поскольку диаграмма переходов специфична для приложения, такого как резервирование авиабилетов, в то время как алгоритм обхода графа более универсален.

Что представляет собой диаграмма переходов? Абстрактно это функция transition, имеющая два аргумента - состояние и выбор пользователя. Функция transition (s, c) возвращает новое состояние, определяемое пользовательским выбором c в состоянии s. Здесь слово "функция" используется в его математическом смысле. На программном уровне можно выбрать реализацию transition либо функцией в программистском смысле, либо структурой данных, например массивом. На данный момент решение можно отложить и рассматривать transition просто как абстрактное понятие.

В добавление к функции transition необходимо спроектировать начальное состояние initial, - точку, в которой начинаются все сессии, и одно или несколько заключительных состояний как булеву функцию is_final. И снова речь идет о функции в математическом смысле, независимо от ее возможной реализации.

Зададим функцию transition в табличной форме со строками, представляющими состояние, и столбцами, отображающими пользовательский выбор:

Таблица 2.1. Таблица переходов
Состояние Выбор
0 1 2 3
1 (Initial) -1 0 5 2
2 (Flights) -1 0 1 3
3 (Seats) 0 2 4
4 (Reserv.) 0 3 5
5 (Confirm) 0 4 1
0 (Help) Return
-1 (Final)

Соглашения, используемые в таблице: здесь в состоянии Help с идентификатором 0 задан специальный переход Return, возвращающий в состояние, запросившее справку, задано также ровно одно финальное состояние -1. Эти соглашения не являются необходимыми для дальнейшего обсуждения, но позволяют проще сделать таблицу.

Архитектура программы

Следуя традиционным рекомендациям декомпозиции сверху вниз, выберем "вершину" - главную функцию нашей системы. Это должна быть, очевидно, программа 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) выводит соответствующее сообщение об ошибке.

Тип ANSWER объекта, представляющего ответ пользователя, не будет уточняться. Значение a этого типа глобально представляет ввод пользователя, включающий и выбор следующего шага ( ANSWER фактически во многом подобен классу, даже если остальная структура не является объектной.)

Для получения работающего приложения необходимо задать реализации программ уровня 1: display, read, correct, message и process.

< Лекция 1 || Лекция 2: 123456 || Лекция 3 >