← Назад к вопросам

Что такое 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

  1. Явное моделирование — все переходы четко определены
  2. Безопасность — невозможны недопустимые переходы
  3. Тестируемость — легко тестировать все сценарии
  4. Документация — граф переходов служит документацией
  5. Масштабируемость — легко добавлять новые состояния

Когда использовать State Machine

Используй State Machine когда:

  • Система имеет четкие состояния и переходы
  • Поведение зависит от текущего состояния
  • Нужна гарантия валидности переходов
  • Сложная бизнес-логика с множеством сценариев

НЕ используй State Machine когда:

  • Система очень простая (несколько состояний)
  • Состояния часто меняются случайно
  • Нет явных переходов между состояниями

Заключение

State Machine — это мощный паттерн для моделирования систем с дискретными состояниями. От простых switch-case решений до complex Spring State Machine, они гарантируют надежность, предсказуемость и легкость тестирования систем любой сложности.