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.
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…elseor
switchstatements 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
UserStatehierarchy.
<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
mainmethod 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><dependency><br/> <groupId>org.springframework.statemachine</groupId><br/> <artifactId>spring-statemachine-core</artifactId><br/> <version>2.0.1.RELEASE</version><br/></dependency></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<OrderStatus, OrderStatusChangeEvent> {<br/> @Override public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {<br/> states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));<br/> }<br/> @Override public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> 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
receiveactions, 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…elselogic 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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.