В UML Distilled Мартина Фаулера , он заявляет (не каламбур) в главе 10 «Схемы конечных автоматов» (выделение мое):
Диаграмма состояний может быть реализована тремя основными способами: вложенный переключатель , шаблон состояния и
таблицы состояний .
Давайте использовать упрощенный пример состояний дисплея мобильного телефона:
Вложенный переключатель
Фаулер привел пример кода на C #, но я адаптировал его к своему примеру.
public void HandleEvent(PhoneEvent anEvent) {
switch (CurrentState) {
case PhoneState.ScreenOff:
switch (anEvent) {
case PhoneEvent.PressButton:
if (powerLow) { // guard condition
DisplayLowPowerMessage(); // action
// CurrentState = PhoneState.ScreenOff;
} else {
CurrentState = PhoneState.ScreenOn;
}
break;
case PhoneEvent.PlugPower:
CurrentState = PhoneState.ScreenCharging;
break;
}
break;
case PhoneState.ScreenOn:
switch (anEvent) {
case PhoneEvent.PressButton:
CurrentState = PhoneState.ScreenOff;
break;
case PhoneEvent.PlugPower:
CurrentState = PhoneState.ScreenCharging;
break;
}
break;
case PhoneState.ScreenCharging:
switch (anEvent) {
case PhoneEvent.UnplugPower:
CurrentState = PhoneState.ScreenOff;
break;
}
break;
}
}
государственный шаблон
Вот реализация моего примера с шаблоном GoF State:
Таблицы состояний
Принимая вдохновение от Фаулера, вот таблица для моего примера:
Source State Target State Event Guard Action
--------------------------------------------------------------------------------------
ScreenOff ScreenOff pressButton powerLow displayLowPowerMessage
ScreenOff ScreenOn pressButton !powerLow
ScreenOn ScreenOff pressButton
ScreenOff ScreenCharging plugPower
ScreenOn ScreenCharging plugPower
ScreenCharging ScreenOff unplugPower
Сравнение
Вложенный переключатель хранит всю логику в одном месте, но код может быть трудно читать при большом количестве состояний и переходов. Возможно, это более безопасно и проще для проверки, чем другие подходы (без полиморфизма или интерпретации).
Реализация шаблона State потенциально может распространить логику на несколько отдельных классов, что может затруднить понимание ее в целом. С другой стороны, маленькие классы легко понять отдельно. Дизайн особенно хрупок, если вы изменяете поведение, добавляя или удаляя переходы, так как они являются методами в иерархии и в коде может быть много изменений. Если вы живете по принципу дизайна небольших интерфейсов, вы увидите, что этот шаблон не очень хорошо работает. Однако, если конечный автомат стабилен, такие изменения не потребуются.
Подход таблиц состояний требует написания некоторого интерпретатора контента (это может быть проще, если у вас есть отражение в языке, который вы используете), что может потребовать много работы заранее. Как указывает Фаулер, если ваша таблица отделена от вашего кода, вы можете изменить поведение своего программного обеспечения без перекомпиляции. Это имеет некоторые последствия для безопасности, однако; программное обеспечение ведет себя на основе содержимого внешнего файла.
Редактировать (не совсем для языка C)
Существует также свободный подход к интерфейсу (он же внутренний предметно-ориентированный язык), которому, вероятно, способствуют языки с первоклассными функциями . Библиотека Stateless существует, и этот блог показывает простой пример с кодом. Обсуждается реализация Java (до Java8) . Мне также показали пример Python на GitHub .