Fundamentals 16 min read

Mastering the State Pattern: Real‑World Java Examples and Best Practices

This article explains the State (State Machine) design pattern, its typical use cases, core roles, and practical Java implementations—including a login‑state example and a Spring StateMachine order workflow—while also comparing it with related patterns and outlining its advantages and drawbacks.

macrozheng
macrozheng
macrozheng
Mastering the State Pattern: Real‑World Java Examples and Best Practices

What is the State Pattern?

The State pattern (also called State Machine pattern) lets an object change its behavior when its internal state changes, making it appear as if its class has changed. It replaces bulky

if…else

or

switch

statements with separate state classes, improving readability and extensibility.

Typical Application Scenarios

When behavior must change with state.

When an operation has many branches that depend on the object's state.

Core 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.

Example 1: User Actions on an Article

When a user reads an article, actions such as forward, collect, and comment are only allowed when logged in. The following code models this with a

UserState

hierarchy.

<code>public abstract class UserState {<br/>    private AppContext appContext;<br/>    public void setAppContext(AppContext appContext) { this.appContext = appContext; }<br/>    public abstract void forward();<br/>    public abstract void collect();<br/>    public abstract void comment(String comment);<br/>}</code>
<code>public class LoginState extends UserState {<br/>    @Override public void forward() { System.out.println("转发成功!"); }<br/>    @Override public void collect() { System.out.println("收藏成功!"); }<br/>    @Override public void comment(String comment) { System.out.println("评论成功, 内容是:" + comment); }<br/>}</code>
<code>public class UnLoginState extends UserState {<br/>    @Override public void forward() { forward2Login(); appContext.forward(); }<br/>    @Override public void collect() { forward2Login(); appContext.collect(); }<br/>    @Override public void comment(String comment) { forward2Login(); appContext.comment(comment); }<br/>    private void forward2Login() {<br/>        System.out.println("跳转到登录页面!");<br/>        appContext.setState(appContext.LOGIN_STATE);<br/>    }<br/>}</code>
<code>public class AppContext {<br/>    public static final UserState LOGIN_STATE = new LoginState();<br/>    public static final UserState UNLOGIN_STATE = new UnLoginState();<br/>    private UserState currentState = UNLOGIN_STATE;<br/>    { LOGIN_STATE.setAppContext(this); UNLOGIN_STATE.setAppContext(this); }<br/>    public void setState(UserState state) { this.currentState = state; this.currentState.setAppContext(this); }<br/>    public void forward() { currentState.forward(); }<br/>    public void collect() { currentState.collect(); }<br/>    public void comment(String comment) { currentState.comment(comment); }<br/>}</code>

A simple

main

method demonstrates the flow.

<code>public static void main(String[] args) {<br/>    AppContext context = new AppContext();<br/>    context.forward();<br/>    context.collect();<br/>    context.comment("说的太好了,双手双脚给个赞👍");<br/>}</code>

Example 2: Order State Management with Spring StateMachine

Spring provides a ready‑made state‑machine component. The following snippets show how to model an e‑commerce order lifecycle.

<code>&lt;dependency&gt;<br/>    &lt;groupId&gt;org.springframework.statemachine&lt;/groupId&gt;<br/>    &lt;artifactId&gt;spring-statemachine-core&lt;/artifactId&gt;<br/>    &lt;version&gt;2.0.1.RELEASE&lt;/version&gt;<br/>&lt;/dependency&gt;</code>
<code>public enum OrderStatus { WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH; }</code>
<code>public enum OrderStatusChangeEvent { PAYED, DELIVERY, RECEIVED; }</code>
<code>@Configuration<br/>@EnableStateMachine(name = "orderStateMachine")<br/>public class OrderStateMachineConfig extends StateMachineConfigurerAdapter&lt;OrderStatus, OrderStatusChangeEvent&gt; {<br/>    @Override public void configure(StateMachineStateConfigurer&lt;OrderStatus, OrderStatusChangeEvent&gt; states) throws Exception {<br/>        states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));<br/>    }<br/>    @Override public void configure(StateMachineTransitionConfigurer&lt;OrderStatus, OrderStatusChangeEvent&gt; transitions) throws Exception {<br/>        transitions.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)<br/>            .and().withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)<br/>            .and().withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);<br/>    }<br/>    @Bean<br/>    public DefaultStateMachinePersister persister() {<br/>        return new DefaultStateMachinePersister<>(new StateMachinePersist<Object, Object, Order>() {<br/>            @Override public void write(StateMachineContext<Object, Object> context, Order order) {}<br/>            @Override public StateMachineContext<Object, Object> read(Order order) {<br/>                return new DefaultStateMachineContext(order.getStatus(), null, null, null);<br/>            }<br/>        });<br/>    }<br/>}</code>

The service layer uses the state machine to process

pay

,

deliver

, and

receive

actions, persisting state between calls and handling concurrency with a synchronized helper method.

Related Patterns

Chain of Responsibility : Both eliminate excessive conditionals; in some cases a state can be treated as a responsibility, allowing either pattern.

Strategy : Their class diagrams are similar, but strategies represent interchangeable algorithms, whereas states are inter‑dependent and transition automatically.

Pros and Cons of the State Pattern

Advantages

Clear structure: each state is a separate class, removing tangled

if…else

logic and improving maintainability.

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

Well‑defined responsibilities and easy extensibility for new behavior within existing states.

Disadvantages

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

Complex implementation: the pattern adds structural overhead and can make the codebase harder to understand if misused.

Limited Open/Closed support: adding a new state often requires modifying transition logic in existing classes.

design patternsJavasoftware architectureState MachineState Pattern
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login 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.