Skip to Content
👋 Добро пожаловать в документацию lite-fsm!
РуководствоКонечные автоматы

Конечные автоматы (FSM)

Конечные автоматы (Finite State Machines, FSM) — это математическая модель поведения системы, которая в каждый момент времени находится в одном из конечного набора состояний.

Примеры reducer-ов на этой странице написаны в стиле Immer: они мутируют draft state и предполагают, что MachineManager создан с immerMiddleware. Без middleware reducer должен вернуть новый объект { state, context }.

Основные компоненты конечного автомата

  1. Состояния — конечный набор возможных состояний системы.
  2. События — действия, которые могут вызвать переход из одного состояния в другое.
  3. Переходы — функции, определяющие новое состояние на основе текущего состояния и события.
  4. Начальное состояние — состояние, в котором система находится до начала работы.

Визуализация конечного автомата

Конечные автоматы часто представляют в виде диаграмм состояний, где:

  • Состояния изображаются как круги или прямоугольники
  • Переходы изображаются как стрелки между состояниями
  • Стрелки помечаются событиями, которые вызывают переход

Пример диаграммы минимального автомата:

+-------+ TURN_ON +-------+ | |------------->| | | OFF | | ON | | |<-------------| | +-------+ TURN_OFF +-------+

Преимущества использования конечных автоматов

1. Предсказуемость и строгая структура

FSM делает поведение системы предсказуемым и ясным. В каждый момент времени система находится в определенном состоянии, и переходы между состояниями чётко определены.

2. Управление сложностью

FSM позволяет разделить сложную логику на явные дискретные состояния и правила перехода между ними.

3. Отладка и тестирование

Модель FSM упрощает отладку и тестирование, потому что:

  • Состояния можно перечислить и проверить
  • Переходы предсказуемы и детерминированы
  • Легко понять, как система перешла в текущее состояние

4. Документирование поведения

Диаграммы состояний FSM могут служить наглядной документацией того, как должна работать система.

Применение конечных автоматов

Конечные автоматы широко используются в различных областях:

  • UI-компоненты: для управления состояниями интерфейса (открыт/закрыт, активен/неактивен)
  • Бизнес-процессы: для моделирования рабочих процессов (новый, в работе, проверка, завершено)
  • Протоколы связи: для управления соединениями (подключение, подключено, отключение)
  • Игровые механики: для управления состояниями игровых объектов (бездействие, перемещение, атака)
  • Парсеры и компиляторы: для разбора и анализа текста

Основные модели реакции на события

При проектировании конечных автоматов важно определить, как система будет реагировать на события. В теории автоматов выделяют две фундаментальные модели:

Автомат Мура

Выходные значения связаны с состояниями, а не с переходами. В концепции statecharts эта модель используется для действий, которые должны выполняться при входе в состояние или пока система находится в определенном состоянии. В lite-fsm эта концепция реализуется через эффекты, привязанные к состояниям:

const lamp = createMachine({ config: { OFF: { TURN_ON: "ON", }, ON: { TURN_OFF: "OFF", }, }, initialState: "OFF", initialContext: { brightness: 0 }, effects: { // Эффект, привязанный к состоянию ON (автомат Мура) ON: ({ light }) => light.turnOn(), }, });

Автомат Мили

Выходные значения связаны с переходами, а не с состояниями. В statecharts это отражается в действиях, которые выполняются во время перехода между состояниями. Такой подход удобен, когда важно реагировать именно на изменение состояния или когда одинаковые события должны вызывать разные действия в зависимости от текущего состояния. В lite-fsm это реализуется через reducer, обрабатывающий конкретные события:

const counter = createMachine({ config: { IDLE: { INCREMENT: "COUNTING", }, COUNTING: { INCREMENT: null, }, }, initialState: "IDLE", initialContext: { count: 0 }, reducer: (state, action, { nextState }) => { state.state = nextState; if (action.type === "INCREMENT") { state.context.count += 1; } }, });

Ограничения базовых конечных автоматов

Плоские конечные автоматы хорошо подходят для моделирования небольших процессов, но при росте системы у них появляются ограничения:

Проблема взрыва состояний

У плоских FSM есть известная проблема — «взрыв состояний». С ростом числа независимых признаков количество возможных комбинаций растёт экспоненциально. Например, 5 независимых булевых флагов дают 2^5 = 32 комбинации, а 10 флагов — уже 1024.

Нарастающая сложность переходов

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

Отсутствие повторного использования логики

В классических FSM сложно выделить повторяющиеся паттерны и переиспользовать их в различных частях приложения, что приводит к дублированию кода и увеличению шансов на ошибки.

Statecharts (Расширенные диаграммы состояний)

Расширенная модель statecharts (диаграммы состояний), предложенная Дэвидом Харелом, решает проблемы простых FSM, добавляя несколько ключевых концепций:

Примечание: Оригинальная статья Дэвида Харела “Statecharts: A Visual Formalism for Complex Systems”  (1987) является фундаментальной работой, определившей концепцию statecharts. Для ознакомления также доступна свободная версия статьи в PDF .

Ключевые концепции Statecharts

1. Иерархия и вложенные состояния

Вложенные (иерархические) состояния группируют связанные состояния внутри родительских. Они используются для:

  • Уменьшения сложности: вместо линейного списка из сотен состояний появляется иерархическая структура, которую проще читать
  • Наследования поведения: Дочерние состояния могут наследовать обработку событий от родительских состояний
  • Модульности: Логика разных функциональных модулей может быть изолирована и разработана независимо

2. Параллельные состояния

Параллельные состояния позволяют моделировать независимые процессы, которые происходят одновременно:

  • Независимые аспекты: Форма может одновременно находиться в состояниях “валидная” и “отправляется”
  • Декомпозиция сложных систем: Разные части UI (панели, диалоги) могут иметь собственные жизненные циклы
  • Предотвращение комбинаторного взрыва: Вместо создания отдельных состояний для каждой комбинации, можно моделировать независимые процессы отдельно

3. Контекст и данные

Расширенная модель включает хранение данных (контекста), что позволяет:

  • Уменьшить количество состояний: Вместо создания отдельных состояний для каждого значения данных, можно хранить их в контексте
  • Сохранять историю: Можно отслеживать предыдущие действия и принимать решения на их основе
  • Принимать условные решения: Переходы могут зависеть от данных в контексте

4. Истории и самопереходы

  • Истории позволяют вернуться к предыдущему активному состоянию после выхода и повторного входа
  • Самопереходы дают возможность обрабатывать события без изменения состояния, но с обновлением контекста

Практические преимущества Statecharts

Применение statecharts и связанных концепций в крупных приложениях даёт практические преимущества:

  1. Масштабируемость: возможность моделировать сложные системы без экспоненциального роста состояний
  2. Понятные границы ответственности: Четкое разделение между различными компонентами и их состояниями
  3. Устранение невозможных состояний: Структурные ограничения предотвращают попадание системы в “невозможные” состояния
  4. Прогнозируемое поведение: Легче отследить, как система перешла в определенное состояние
  5. Упрощение тестирования: Возможность тестировать отдельные компоненты системы независимо
  6. Документирование системы: Диаграмма statechart сама по себе является наглядной документацией поведения системы

Создание базового автомата

import { createMachine, MachineManager } from "@lite-fsm/core"; const player = createMachine({ config: { IDLE: { WALK: "WALKING", RUN: "RUNNING", }, WALKING: { STOP: "IDLE", SPEED_UP: "RUNNING", }, RUNNING: { STOP: "IDLE", SLOW_DOWN: "WALKING", }, }, initialState: "IDLE", initialContext: { speed: 0, direction: null as null | "north" | "south" | "east" | "west", }, }); const manager = MachineManager({ player }); manager.transition({ type: "WALK", payload: { direction: "north", speed: 3 } });

Расширенные возможности и паттерны

Иерархические состояния через композицию

В lite-fsm иерархические состояния моделируются с помощью композиции автоматов:

// Вложенный автомат для режимов редактирования const editor = createMachine({ config: { TEXT: { SWITCH_MODE: "VISUAL", SAVE: null, }, VISUAL: { SWITCH_MODE: "TEXT", SAVE: null, }, }, initialState: "TEXT", initialContext: { content: "" }, }); // Основной автомат, который управляет текущим режимом работы const document = createMachine({ config: { VIEWING: { EDIT: "EDITING", }, EDITING: { SAVE: "VIEWING", CANCEL: "VIEWING", }, }, initialState: "VIEWING", initialContext: {}, }); const manager = MachineManager({ document, editor });

Связанные состояния через композицию

Пример двух машин, которые реагируют на одно событие и вместе описывают процесс:

const uploadFlow = createMachine({ config: { WAITING: { START_UPLOAD: "UPLOADING", }, UPLOADING: { UPLOAD_DONE: "COMPLETE", }, COMPLETE: {}, }, initialState: "WAITING", initialContext: {}, }); const uploadProgress = createMachine({ config: { INACTIVE: { START_UPLOAD: "TRACKING", }, TRACKING: { PROGRESS: null, UPLOAD_DONE: "INACTIVE", }, }, initialState: "INACTIVE", initialContext: { percent: 0 }, reducer: (state, action, { nextState }) => { state.state = nextState; if (action.type === "PROGRESS") state.context.percent = action.payload.percent; if (action.type === "START_UPLOAD") state.context.percent = 0; }, }); const manager = MachineManager({ uploadFlow, uploadProgress }); // Одно событие обрабатывают оба автомата manager.transition({ type: "START_UPLOAD" });

Параллельные состояния

Параллельные состояния реализуются через независимые автоматы:

const playback = createMachine({ config: { PLAYING: { PAUSE: "PAUSED", }, PAUSED: { PLAY: "PLAYING", }, }, initialState: "PAUSED", initialContext: {}, }); const audio = createMachine({ config: { MUTED: { UNMUTE: "UNMUTED", }, UNMUTED: { MUTE: "MUTED", VOLUME_CHANGE: null, }, }, initialState: "UNMUTED", initialContext: { volume: 50 }, }); const manager = MachineManager({ playback, audio });

Самопереходы

Самопереходы позволяют обрабатывать события без изменения состояния:

const draft = createMachine({ config: { EDITING: { UPDATE_TITLE: null, // Самопереход — остаёмся в EDITING SUBMIT: "SUBMITTED", }, SUBMITTED: {}, }, initialState: "EDITING", initialContext: { title: "" }, reducer: (state, action, { nextState }) => { state.state = nextState; if (action.type === "UPDATE_TITLE") { state.context.title = action.payload.title; } }, });

Финальные состояния

Финальные состояния моделируются как состояния без исходящих переходов:

const process = createMachine({ config: { IDLE: { START: "PROCESSING", }, PROCESSING: { COMPLETE: "COMPLETED", ERROR: "FAILED", }, COMPLETED: {}, // Финальное состояние — нет исходящих переходов FAILED: { RETRY: "PROCESSING", }, }, initialState: "IDLE", initialContext: {}, });

Условные переходы

Условные переходы реализуются с помощью reducer:

const auth = createMachine({ config: { CHECKING: { AUTH_SUCCESS: "AUTHENTICATED", AUTH_FAILURE: "ERROR", }, AUTHENTICATED: { LOGOUT: "LOGGED_OUT", }, ERROR: { RETRY: "CHECKING", }, LOGGED_OUT: { LOGIN: "CHECKING", }, }, initialState: "CHECKING", initialContext: { user: null as null | { id: string }, isAdmin: false, }, reducer: (state, action, { nextState }) => { // Guard: события без admin-роли не меняют состояние и контекст if (action.type === "AUTH_SUCCESS" && action.payload.role !== "admin") { return; } state.state = nextState; if (action.type === "AUTH_SUCCESS") { state.context.user = action.payload.user; state.context.isAdmin = true; } }, });

Заключение

Моделирование поведения с помощью конечных автоматов и statecharts делает состояния и переходы приложения явными. Выбор между плоской FSM, композицией автоматов и statecharts-моделью с иерархией и параллельными состояниями зависит от сложности процесса.

lite-fsm не предоставляет прямую поддержку всех возможностей statecharts в одном автомате, например вложенных состояний внутри одной машины. Для таких сценариев библиотека предлагает композицию автоматов, разделение ответственности и систему effects.

Last updated on