Иво Салмре - Программирование мобильных устройств на платформе .NET Compact Framework
На рис. 5.1 в схематическом виде представлен конечный автомат обучающей игры, позволяющей изучать иностранные слова путем выбора одного из предлагаемых вариантов ответа. Поведение приложения разбито на пять логических состояний. Эти логические состояния существуют независимо от того, будут ли они выбраны нами для введения их определений в конечный автомат. Конечный автомат — это просто формальное описание того, как работает приложение. Она позволяет нам ясно представлять себе различные состояния, в которых может находиться приложение в процессе взаимодействия с ним пользователя.
Другим способом представления состояний приложения является построение диаграммы переходов. Эта диаграмма содержит список дискретных состояний, в которых может находиться приложение, а также возможные варианты переходов между состояниями. Список возможных состояний и вариантов перехода из одного состояния в другое для нашего приложения представлен в табл. 5.1.
Рис. 5.1. Конечный автомат для простой обучающей игры, предназначенной для изучения иностранных слов путем выбора правильного ответа из нескольких предложенных вариантов
Таблица 5.1. Варианты изменения состояний для словарной игры с множественным выбором
Состояние Внешний ввод Следующее состояние StartScreen Пользователь выбрал вариант "Next Question" AskQuestion StartScreen Пользователь выбрал вариант "Correct Answer" Запрещенный переход! StartScreen Пользователь выбрал вариант "Incorrect Answer" Запрещенный переход! StartScreen Пользователь выбрал вариант "End Game" Запрещенный переход! AskQuestion Пользователь выбрал вариант "Next Question" Запрещенный переход! AskQuestion Пользователь выбрал вариант "Correct Answer" CongratulateUser AskQuestion Пользователь выбрал вариант "Incorrect Answer" ScoldUser AskQuestion Пользователь выбрал вариант "End Game" Запрещенный переход! CongratulateUser Пользователь выбрал вариант "Next Question" AskQuestion CongratulateUser Пользователь выбрал вариант "Correct Answer" Запрещенный переход! CongratulateUser Пользователь выбрал вариант "Incorrect Answer" Запрещенный переход! CongratulateUser Пользователь выбрал вариант "End Game" StartScreen ScoldUser Пользователь выбрал вариант "Next Question" AskQuestion ScoldUser Пользователь выбрал вариант "Correct Answer" Запрещенный переход! ScoldUser Пользователь выбрал вариант "Incorrect Answer" Запрещенный переход! ScoldUser Пользователь выбрал вариант "End Game" StartScreen НА ЗАМЕТКУЯ решил привести в таблице все возможные перестановки переменных, указанных в столбцах "Состояние" и "Внешний ввод", чтобы проиллюстрировать тот факт, что не любое изменение состояния разрешено. Переходы между состояниями, указанными в строках, для которых в столбце "Следующее состояние" содержится пояснение "Запрещенный переход!", недопустимы в нашем приложении. Если приложение пытается каким-либо образом осуществить такое изменение состояния, значит, в логике его выполнения присутствуют ошибки. В случае если предпринимается попытка выполнения недействительного перехода, логика конечного автомата должна возбуждать исключение или, по крайней мере, использовать оператор ASSERT в режиме отладки. Явная идентификация запрещенных переходов между состояниями облегчает отладку приложений.
В листинге 5.1 представлен код, реализующий определенный выше конечный автомат. Этот код соответствует диаграмме переходов, представленной в табл. 5.1 и на рис. 5.1. Обратите внимание на то, что в приведенной ниже функции участки кода, соответствующие различным изменениям состояний, содержат вызовы функций, отключенные с помощью символов комментариев. Эти функциональные вызовы представляют ту часть работы, которая должна быть выполнена для соответствующего изменения состояния, и отключены символами комментариев с той целью, чтобы приведенный ниже код можно было компилировать как независимый блок; реализация вызываемых функций остается за вами. Для определения текущего варианта изменения состояния удобно использовать блок операторов switch/case.
Каждый оператор case соответствует одному из вариантов изменения состояния и должен содержать вызов функции, выполняющей всю необходимую для этого работу. Подобного рода централизация и инкапсуляция управления состояниями является одним из наиболее мощных аспектов использования конечных автоматов; все важные изменения состояний приложения определяются и обрабатываются централизованно в одном месте программы.
Листинг 5.1. Простой код конечного автомата для игры с множественным выборомclass MyStateMachineClass {
private enum GameState {
StartScreen, AskQuestion, CongratulateUser, ScoldUser
}
private GameState m_CurrentGameState;
//---------------------------------------------------------------------
//Конечный автомат, воздействующий на пользовательский интерфейс
//и управляющий переходами приложения в другие состояния в соответствии
//c текущим режимом работы пользователя
//---------------------------------------------------------------------
private void StateChangeForGame(GameState newGameUIState) {
//Определить, в какое состояние переходит приложение
switch(newGameUIState) {
case GameState.StartScreen:
//Если переход в данное состояние осуществляется из состояния,
//для которого это запрещено, возбудить исключение
if ((m_CurrentGameState != GameState.CongratulateUser) && (m_CurrentGameState != GameState.ScoldUser)) {
throw new System.Exception("Запрещённый переход!");
}
//ЧТО СДЕЛАТЬ: Поместите сюда код, выполняющий следующие операции:
// 1. Скрытие (Hide), отображение (Show) и перемещение (Move)
// элементов управления пользовательского интерфейса
// 2. Настройка переменных/состояния игры, соответствующих
// данному режиму работы
//
// SetUpGameStateForStartScreen();
break;
case GameState.AskQuestion:
//Если переход в данное состояние осуществляется из состояния,
//для которого это запрещено, возбудить исключение
if ((m_CurrentGameState != GameState.StartScreen)
&& (m_CurrentGameState != GameState.CongratulateUser)
&& (m_CurrentGameState !=GameState.ScoldUser)) {
throw new System.Exception("Запрещённый переход!");
}
//ЧТО СДЕЛАТЬ: Поместите сюда код, выполняющий следующие операции:
// 1. Скрытие (Hide), отображение (Show) и перемещение (Move)
// элементов управления пользовательского интерфейса
// 2. Настройка переменных/состояния игры, соответствующих
// данному режиму работы
//
// SetUpGameStateForAskQuestion();
break;
case GameState.CongratulateUser:
//Если переход в данное состояние осуществляется из состояния,
//для которого это запрещено, возбудить исключение
if (m_CurrentGameState != GameState.AskQuestion) {
throw new System.Exception("Запрещённый переход!");
}
//ЧТО СДЕЛАТЬ: Поместите сюда код, выполняющий следующие операции:
// 1. Скрытие (Hide), отображение (Show) и перемещение (Move)
// элементов управления пользовательского интерфейса
// 2. Настройка переменных/состояния игры, соответствующих
// данному режиму работы
//