← Назад к вопросам
Что такое State machine?
1.8 Middle🔥 131 комментариев
#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое State Machine
State Machine (конечный автомат, конечная машина состояний) — это математическая абстракция, описывающая систему, которая может находиться в одном из конечного набора состояний и переходить между ними на основе событий и условий. State Machine критична для создания надежных, предсказуемых систем в Java.
Основные концепции
Компоненты State Machine
Состояния (States):
INIT → PROCESSING → COMPLETED
↓
FAILED → RETRY → PROCESSING
Переходы (Transitions):
Event: START → переход INIT → PROCESSING
Event: COMPLETE → переход PROCESSING → COMPLETED
Event: ERROR → переход PROCESSING → FAILED
Действия (Actions):
При входе в состояние
При выходе из состояния
При выполнении события
Простой пример: заказ в e-commerce
public enum OrderState {
CREATED, // Заказ создан
PAID, // Оплачен
PROCESSING, // В обработке
SHIPPED, // Отправлен
DELIVERED, // Доставлен
CANCELLED // Отменен
}
public enum OrderEvent {
PAY,
PROCESS,
SHIP,
DELIVER,
CANCEL
}
// Базовая реализация State Machine
public class SimpleOrderStateMachine {
private OrderState currentState = OrderState.CREATED;
public void processEvent(OrderEvent event) {
switch (currentState) {
case CREATED:
if (event == OrderEvent.PAY) {
currentState = OrderState.PAID;
} else if (event == OrderEvent.CANCEL) {
currentState = OrderState.CANCELLED;
} else {
throw new IllegalStateException("Invalid transition");
}
break;
case PAID:
if (event == OrderEvent.PROCESS) {
currentState = OrderState.PROCESSING;
} else if (event == OrderEvent.CANCEL) {
currentState = OrderState.CANCELLED;
} else {
throw new IllegalStateException("Invalid transition");
}
break;
case PROCESSING:
if (event == OrderEvent.SHIP) {
currentState = OrderState.SHIPPED;
} else if (event == OrderEvent.CANCEL) {
currentState = OrderState.CANCELLED;
} else {
throw new IllegalStateException("Invalid transition");
}
break;
case SHIPPED:
if (event == OrderEvent.DELIVER) {
currentState = OrderState.DELIVERED;
} else {
throw new IllegalStateException("Invalid transition");
}
break;
default:
throw new IllegalStateException("Cannot process event in state: " + currentState);
}
}
public OrderState getCurrentState() {
return currentState;
}
}
Продвинутая реализация: State Pattern
import java.util.HashMap;
import java.util.Map;
// Интерфейс для каждого состояния
public interface OrderStateHandler {
void onEnter();
void onExit();
void pay(Order order);
void process(Order order);
void ship(Order order);
void deliver(Order order);
void cancel(Order order);
}
// Реализация каждого состояния
public class CreatedState implements OrderStateHandler {
private Order order;
public CreatedState(Order order) {
this.order = order;
}
@Override
public void onEnter() {
System.out.println("Order created");
}
@Override
public void onExit() {
System.out.println("Leaving created state");
}
@Override
public void pay(Order order) {
order.setState(new PaidState(order));
System.out.println("Payment processed");
}
@Override
public void cancel(Order order) {
order.setState(new CancelledState(order));
System.out.println("Order cancelled");
}
@Override
public void process(Order order) {
throw new IllegalStateException("Cannot process unpaid order");
}
@Override
public void ship(Order order) {
throw new IllegalStateException("Cannot ship unprocessed order");
}
@Override
public void deliver(Order order) {
throw new IllegalStateException("Cannot deliver unshipped order");
}
}
public class PaidState implements OrderStateHandler {
private Order order;
public PaidState(Order order) {
this.order = order;
}
@Override
public void onEnter() {
System.out.println("Order paid");
}
@Override
public void onExit() {
System.out.println("Leaving paid state");
}
@Override
public void process(Order order) {
order.setState(new ProcessingState(order));
System.out.println("Order is being processed");
}
@Override
public void cancel(Order order) {
order.setState(new CancelledState(order));
System.out.println("Order cancelled, refund initiated");
}
@Override
public void pay(Order order) {
throw new IllegalStateException("Order is already paid");
}
@Override
public void ship(Order order) {
throw new IllegalStateException("Order not processed yet");
}
@Override
public void deliver(Order order) {
throw new IllegalStateException("Order not shipped yet");
}
}
// Класс Order с State Machine
public class Order {
private Long id;
private OrderStateHandler state;
public Order(Long id) {
this.id = id;
this.state = new CreatedState(this);
this.state.onEnter();
}
public void setState(OrderStateHandler newState) {
this.state.onExit();
this.state = newState;
this.state.onEnter();
}
public void pay() {
state.pay(this);
}
public void process() {
state.process(this);
}
public void ship() {
state.ship(this);
}
public void deliver() {
state.deliver(this);
}
public void cancel() {
state.cancel(this);
}
}
Spring State Machine Framework
Для сложных государственных машин используют Spring State Machine:
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
// Определяем состояния и события
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {
states
.withStates()
.initial(OrderState.CREATED)
.states(EnumSet.allOf(OrderState.class))
.end(OrderState.DELIVERED)
.end(OrderState.CANCELLED);
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions
// Переход из CREATED в PAID при событии PAY
.withExternal()
.source(OrderState.CREATED)
.target(OrderState.PAID)
.event(OrderEvent.PAY)
.action(context -> System.out.println("Processing payment"))
.and()
// Переход из PAID в PROCESSING при событии PROCESS
.withExternal()
.source(OrderState.PAID)
.target(OrderState.PROCESSING)
.event(OrderEvent.PROCESS)
.action(context -> System.out.println("Starting processing"))
.and()
// Переход из PROCESSING в SHIPPED при событии SHIP
.withExternal()
.source(OrderState.PROCESSING)
.target(OrderState.SHIPPED)
.event(OrderEvent.SHIP)
.action(context -> System.out.println("Order shipped"))
.and()
// Переход из SHIPPED в DELIVERED при событии DELIVER
.withExternal()
.source(OrderState.SHIPPED)
.target(OrderState.DELIVERED)
.event(OrderEvent.DELIVER)
.action(context -> System.out.println("Order delivered"))
.and()
// Из любого состояния можно отменить заказ
.withExternal()
.source(OrderState.CREATED)
.target(OrderState.CANCELLED)
.event(OrderEvent.CANCEL);
}
}
// Использование State Machine
@Service
public class OrderService {
@Autowired
private StateMachine<OrderState, OrderEvent> stateMachine;
public void payOrder(Long orderId) {
stateMachine.sendEvent(OrderEvent.PAY);
}
public void processOrder(Long orderId) {
stateMachine.sendEvent(OrderEvent.PROCESS);
}
public void shipOrder(Long orderId) {
stateMachine.sendEvent(OrderEvent.SHIP);
}
public OrderState getCurrentState() {
return stateMachine.getState().getId();
}
}
Граждане State Machine в Java
1. Hierarchical State Machines (вложенные состояния)
ORDER_STATE
├── INITIAL
├── PROCESSING (суперсостояние)
│ ├── VALIDATING
│ ├── SHIPPING
│ └── INSURANCE
├── COMPLETED
└── CANCELLED
2. Guarded Transitions (условные переходы)
.withExternal()
.source(OrderState.CREATED)
.target(OrderState.PAID)
.event(OrderEvent.PAY)
.guard(context -> context.getExtendedState().getVariables().get("amount") != null)
.action(context -> System.out.println("Payment processed"))
Реальный пример: Workflow заявки
public enum TicketState {
NEW,
ASSIGNED,
IN_PROGRESS,
WAITING_FOR_INFO,
RESOLVED,
CLOSED
}
public enum TicketEvent {
ASSIGN,
START,
REQUEST_INFO,
RESOLVE,
REOPEN,
CLOSE
}
@Entity
public class Ticket {
@Id
private Long id;
@Enumerated(EnumType.STRING)
private TicketState state;
private String title;
private String description;
private Long assigneeId;
@Transactional
public void transition(TicketEvent event) {
switch (state) {
case NEW:
if (event == TicketEvent.ASSIGN) {
state = TicketState.ASSIGNED;
}
break;
case ASSIGNED:
if (event == TicketEvent.START) {
state = TicketState.IN_PROGRESS;
}
break;
case IN_PROGRESS:
if (event == TicketEvent.REQUEST_INFO) {
state = TicketState.WAITING_FOR_INFO;
} else if (event == TicketEvent.RESOLVE) {
state = TicketState.RESOLVED;
}
break;
case WAITING_FOR_INFO:
if (event == TicketEvent.START) {
state = TicketState.IN_PROGRESS;
}
break;
case RESOLVED:
if (event == TicketEvent.REOPEN) {
state = TicketState.IN_PROGRESS;
} else if (event == TicketEvent.CLOSE) {
state = TicketState.CLOSED;
}
break;
}
}
}
Преимущества State Machine
- Явное моделирование — все переходы четко определены
- Безопасность — невозможны недопустимые переходы
- Тестируемость — легко тестировать все сценарии
- Документация — граф переходов служит документацией
- Масштабируемость — легко добавлять новые состояния
Когда использовать State Machine
✓ Используй State Machine когда:
- Система имеет четкие состояния и переходы
- Поведение зависит от текущего состояния
- Нужна гарантия валидности переходов
- Сложная бизнес-логика с множеством сценариев
✗ НЕ используй State Machine когда:
- Система очень простая (несколько состояний)
- Состояния часто меняются случайно
- Нет явных переходов между состояниями
Заключение
State Machine — это мощный паттерн для моделирования систем с дискретными состояниями. От простых switch-case решений до complex Spring State Machine, они гарантируют надежность, предсказуемость и легкость тестирования систем любой сложности.