Fundamentals 19 min read

Master the State Pattern: Java Basics to Spring StateMachine Order Flow

This article explains the State design pattern, illustrates its advantages over traditional conditional logic, provides a complete Java implementation with abstract state, concrete states, and context classes, demonstrates a real‑world order processing scenario using Spring StateMachine, and discusses related patterns, benefits, and drawbacks.

Architect
Architect
Architect
Master the State Pattern: Java Basics to Spring StateMachine Order Flow

State pattern is common in everyday scenarios such as order status changes in online shopping or elevator floor changes.

In software development, handling multiple conditions with if/else or switch statements leads to bulky, hard‑to‑read code that is difficult to extend. By representing each state as a separate class, the system can delegate behavior to the appropriate state object, eliminating complex conditionals and improving extensibility.

State Pattern (also known as State Machine Pattern) allows an object to change its behavior when its internal state changes, making it appear as if its class has changed. The core idea is binding states to behaviors, with different behaviors for different states.

1. Application Scenarios of State Pattern

State pattern is suitable for the following situations:

Behaviors that change with state.

Operations with many branches that depend on the object's state.

The pattern involves three roles:

Context : defines the client interface, holds a reference to the current state, and handles state transitions.

State (abstract) : declares behavior associated with a particular state.

ConcreteState : implements behavior for a specific state and may trigger state changes.

1.1 State Pattern in Business Scenario

When a user reads an article and wants to comment, share, or favorite it, the actions depend on whether the user is logged in. If not, the system redirects to a login page before performing the action. The two states are LoggedIn and NotLoggedIn , with three possible actions.

public abstract class UserState {
    private AppContext appContext;
    public void setAppContext(AppContext appContext) { this.appContext = appContext; }
    public abstract void forward();
    public abstract void collect();
    public abstract void comment(String comment);
}

Login state implementation:

public class LoginState extends UserState {
    @Override
    public void forward() { System.out.println("转发成功!"); }
    @Override
    public void collect() { System.out.println("收藏成功!"); }
    @Override
    public void comment(String comment) { System.out.println("评论成功,内容是:" + comment); }
}

Unlogin state implementation:

public class UnLoginState extends UserState {
    @Override
    public void forward() { forward2Login(); this.appContext.forward(); }
    @Override
    public void collect() { forward2Login(); this.appContext.collect(); }
    @Override
    public void comment(String comment) { forward2Login(); this.appContext.comment(comment); }
    private void forward2Login() { System.out.println("跳转到登录页面!"); this.appContext.setState(this.appContext.LOGIN_STATE); }
}

Context class:

public class AppContext {
    public static final UserState LOGIN_STATE = new LoginState();
    public static final UserState UNLOGIN_STATE = new UnLoginState();
    private UserState currentState = UNLOGIN_STATE;
    {
        UNLOGIN_STATE.setAppContext(this);
        LOGIN_STATE.setAppContext(this);
    }
    public void setState(UserState state) { this.currentState = state; this.currentState.setAppContext(this); }
    public void forward() { this.currentState.forward(); }
    public void collect() { this.currentState.collect(); }
    public void comment(String comment) { this.currentState.comment(comment); }
}

Test main method:

public static void main(String[] args) {
    AppContext context = new AppContext();
    context.forward();
    context.collect();
    context.comment("说的太好了,双手双脚给个赞👍");
}
(The original article shows an image of the program output here.)

1.2 Using State Machine for Order Flow Control

The state machine is a specialized form of the state pattern, often used in workflows or games. Spring provides a StateMachine component that simplifies state control.

1. pom dependency

<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

2. Create Order Entity

public class Order {
    private int id;
    private OrderStatus status;
    public void setStatus(OrderStatus status) { this.status = status; }
    public OrderStatus getStatus() { return status; }
    public void setId(int id) { this.id = id; }
    public int getId() { return id; }
    @Override
    public String toString() { return "订单号:" + id + ", 订单状态:" + status; }
}

3. Create OrderStatus and OrderStatusChangeEvent enums

public enum OrderStatus { WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH; }
public enum OrderStatusChangeEvent { PAYED, DELIVERY, RECEIVED; }

4. Configure State Transitions

@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
        states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));
    }
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {
        transitions.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
            .and().withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
            .and().withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
    }
    @Bean
    public DefaultStateMachinePersister persister() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist<Object, Object, Order>() {
            @Override
            public void write(StateMachineContext<Object, Object> context, Order order) {}
            @Override
            public StateMachineContext<Object, Object> read(Order order) {
                return new DefaultStateMachineContext(order.getStatus(), null, null, null);
            }
        });
    }
}

5. Add Order State Listener

@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
public class OrderStateListenerImpl {
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public boolean payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.WAIT_DELIVER);
        System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public boolean deliverTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.WAIT_RECEIVE);
        System.out.println("发货,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public boolean receiveTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.FINISH);
        System.out.println("收货,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
}

6. Define IOrderService Interface

public interface IOrderService {
    Order create();
    Order pay(int id);
    Order deliver(int id);
    Order receive(int id);
    Map<Integer, Order> getOrders();
}

7. Implement Service Logic

@Service("orderService")
public class OrderServiceImpl implements IOrderService {
    @Autowired private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    @Autowired private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, Order> persister;
    private int id = 1;
    private Map<Integer, Order> orders = new HashMap<>();
    public Order create() {
        Order order = new Order();
        order.setStatus(OrderStatus.WAIT_PAYMENT);
        order.setId(id++);
        orders.put(order.getId(), order);
        return order;
    }
    public Order pay(int id) { /* send PAYED event */ return orders.get(id); }
    public Order deliver(int id) { /* send DELIVERY event */ return orders.get(id); }
    public Order receive(int id) { /* send RECEIVED event */ return orders.get(id); }
    public Map<Integer, Order> getOrders() { return orders; }
    private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) {
        boolean result = false;
        try {
            orderStateMachine.start();
            persister.restore(orderStateMachine, order);
            Thread.sleep(1000);
            result = orderStateMachine.sendEvent(message);
            persister.persist(orderStateMachine, order);
        } catch (Exception e) { e.printStackTrace(); }
        finally { orderStateMachine.stop(); }
        return result;
    }
}

Test main method demonstrates concurrent order processing and prints final order states.

2. Source Code Reflection of State Pattern

The State pattern also appears in framework source code, such as the JSF PhaseId class, which defines lifecycle phases as distinct constants.

public class PhaseId implements Comparable {
    private final int ordinal;
    private String phaseName;
    private static int nextOrdinal = 0;
    public static final PhaseId ANY_PHASE = new PhaseId("ANY");
    public static final PhaseId RESTORE_VIEW = new PhaseId("RESTORE_VIEW");
    // ... other phases ...
    private PhaseId(String newPhaseName) { this.ordinal = nextOrdinal++; this.phaseName = newPhaseName; }
    public int compareTo(Object other) { return this.ordinal - ((PhaseId) other).ordinal; }
    public String toString() { return phaseName == null ? String.valueOf(ordinal) : phaseName + ' ' + ordinal; }
    static { /* initialize values array */ }
}

These phases are used by the JSF lifecycle to manage request processing.

3. Related Patterns

3.1 State Pattern vs. Chain of Responsibility

Both patterns reduce excessive conditional logic. In some cases, a state can be treated as a responsibility, allowing either pattern to be applied. The key difference is that in the State pattern each state knows its next state, whereas in a chain the client assembles the sequence.

3.2 State Pattern vs. Strategy Pattern

The UML structures of State and Strategy are almost identical, but their intents differ. Strategy selects one of many independent algorithms, while State models an object whose behavior changes as its internal state changes, with automatic transitions that the client cannot directly control.

4. Advantages and Disadvantages of the State Pattern

Clear structure : separates states into classes, removing bulky if/else, improving maintainability.

Explicit state transitions : using distinct classes makes state changes more visible than simple numeric assignments.

Well‑defined responsibilities and extensibility of each state class.

Class explosion : many possible states can lead to a large number of classes.

Complex implementation : the pattern adds structural and code complexity, which can cause confusion if misused.

Limited Open/Closed Principle support : adding new states often requires modifying transition logic, and changing behavior of existing states may need source changes.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsJavaorder processingState Patternspring statemachine
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.