Листинг 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. Настройка переменных/состояния игры, соответствующих
// данному режиму работы
//
// SetUpGameStateForCongratulateUser();
break;
case GameState.ScoldUser:
//Если переход в данное состояние осуществляется из состояния,
//для которого это запрещено, возбудить исключение
if (m_CurrentGameState != GameState.AskQuestion) {
throw new System.Exception("Запрещённый переход!");
}
//ЧТО СДЕЛАТЬ: Поместите сюда код, выполняющий следующие операции:
// 1. Скрытие (Hide), отображение (Show) и перемещение (Move)
// элементов управления пользовательского интерфейса
// 2. Настройка переменных/состояния игры, соответствующих
// данному режиму работы
//
// SetUpGameStateForScoldUser();
break;
default:
throw new System.Exception("Неизвестное состояние!");
}
//Сохранить запрошенное новое состояние в качестве текущего
m_CurrentGameState = newGameUIState;
}
} //Конец класса
Явно и неявно определенные конечные автоматы
Планируете ли вы это или не планируете, но ваш код будет так или иначе управляться состояниями. Например, если какой-либо элемент управления необходимо сделать недоступным для пользователя, то разработчики часто добиваются этого, устанавливая для свойств Enabled и Visible этих элементов управления значение false (например, TextBox1.Visible = false;). Для написания кода такого типа существует два возможных подхода, которые рассматриваются ниже
Подход 1: зависящее от специфики конкретной ситуации, децентрализованное, неявное управление состояниями (неудачный подход)
Специализированный стиль проектирования, ориентированный на максимально возможный учет специфики конкретной задачи, часто встречается в тех случаях, когда приложение в процессе разработки постепенно усложняется. Различные аспекты состояния приложения изменяются в разных местах приложения. Данные о состоянии хранятся в таких свойствах элементов управления, как Visible, Enabled, Size или Position. Переменные, используемые для хранения ключевой информации о состоянии, изменяются непосредственно в тех строках кода, где это оказывается необходимым, а загрузка данных и освобождение памяти от них распределяются по всему приложению в зависимости от конкретной ситуации. Развитие событий напоминает "перетягивание каната" между различными частями приложения, поскольку каждая из функций делает все необходимое для выполнения возложенных на нее задач, не обращая никакого внимания на остальную часть приложения. Простейшим примером подобного поведения может служить код, реагирующий на такие события пользовательского интерфейса, как щелчок на кнопке, которой соответствует встроенный код, изменяющий состояние приложения. В листинге 5.2 приведен типичный код, встречающийся в классах формы приложения, соответствующих описанному подходу. Как код события загрузки формы form1, так и код события щелчка кнопки button1 вносят изменения, которые влияют на общее состояние формы.
Читать дальше