State Machine: когда State уже недостаточно
Паттерн State отлично подходит для управления поведением объекта в зависимости от его состояния. Но что делать, когда логика переходов между состояниями усложняется?
Например в наших требованиях появляются:
- Множественные события перехода в одно состояние.
- Сложные условия перехода, зависящие от внешних факторов.
- Асинхронные переходы - например, ожидание ответа от API.
- Необходимость мониторинга и/или отката состояний (нужна история состояний).
- Динамическая конфигурация состояний - логика состояний должна быть гибкой и изменяемой без перекомпиляции кода.
В таких случаях подходящим решением будет использование - State Machine/Finite State Mashine (FSM - Конечный автомат).
🏛 История появления FSM
Идея конечного автомата (FSM) уходит корнями в математику и теорию автоматов 1940–1950-х годов. Она была описана в работах таких учёных, как Алан Тьюринг, Стивен Клини и Майкл Рабин, как модель вычислений.
В 1956 году — Джордж Мили и Эдвард Мур (не путать с Гордоном Муром из Intel) независимо друг от друга формализовали конечные автоматы (Finite State Machines, FSM):
- Автомат Мили — выход зависит от состояния и входа.
- Автомат Мура — выход зависит только от состояния. Эти модели стали основой для проектирования цифровых схем, компиляторов, сетевых протоколов и теории формальных языков.
FSM стали основой цифровой логики, компиляторов, сетевых протоколов и теории формальных языков. Стоит отметить: В книге "Design Patterns: Elements of Reusable Object-Oriented Software" (банда четырёх, GoF) описан паттерн State, но не State Machine/FSM.
⚙ Преимущества и недостатки State Machine
✅ Преимущества:
- Централизованная логика и наглядная модель переходов между состояниями.
- Легко визуализировать (например, с помощью диаграмм состояний). Некоторые реализации FSM позволяют генерировать диаграммы по коду.
- Удобно использовать для отладки и логирования.
- Поддерживаются сложные переходы, события, действия и условные переходы.
- Хорошо масштабируется.
- Легко добавлять новые состояния и переходы, не затрагивая существующую логику.
- Много готовых реализаций.
❌ Недостатки:
- Требует больше кода и архитектурной подготовки.
- Может быть избыточна для простых сценариев.
- Реализация может быть очень громоздкой.
- Если машина состояний слишком большая, её сложно читать и отлаживать.
💡 Интересная реализация State Machine - Табличная (Table-Driven) реализация
В этой реализации вместо отдельных классов для каждого состояния используется таблица (Map>, но можно использовать и массив), которая хранит информацию о переходах состояний и связанных с ними действиях.
Сначала определим основы:
Теперь определим State Machine с таблицей переходов:
🕹 Тестирование и запуск:
💎 Преимущества такого подхода:
- Наглядная таблица переходов — легко читать, расширить или загрузить из конфигурационных файлов (YAML/JSON).
- Изоляция разработчиков — легко тестируются и не захламляют переходную логику.В отличие от классического switch, добавление нового состояния — просто расширение таблицы.
- Возможность использовать одну и ту же таблицу переходов, но разные обработчики.
📌 Некоторые выводы
Паттерн State Machine — это подходящий инструмент для моделирования систем, поведение которых сильно зависит от их состояний и логики переходов между ними. Он становится незаменимым, когда State уже не справляется с растущей сложностью. Когда логика переходов и состояний усложняется, стоит рассмотреть готовые реализации, которые предлагают множество дополнительных полезных обвязок - сохранение состояния в БД, визуализация диаграмм состояний и т.п. Одну из таких реализаций - spring state machine мы рассмотрим в следующих постах.
Кто реализовывал State Machine самостоятельно?
Канал автора в telegram