Java设计模式之《状态模式》
目录
1、状态模式
1.1、介绍
1.2、设计背景
1.3、适用场景
2、实现
2.1、if-else实现
2.2、状态模式实现
2.3、最终版
1、关于if-else的优化
2、状态模式下的优化
3、ArrayList 配置“状态流”
3、总结
前言
关于Java的设计模式分类如下:
对于状态模式的组件组成,如下所示:
1、状态模式
1.1、介绍
允许一个对象在其内部状态发生改变时,行为也随之改变,看起来就像对象修改了它的类。
- 状态模式就是把“状态”封装成独立的类,不同状态下执行不同的逻辑
- 状态切换由对象自己控制,而不是外部用 if-else/switch来判断
1.2、设计背景
问题场景:
if (status == "UNPAID") { /* 处理支付 */ }
else if (status == "PAID") { /* 处理发货 */ }
else if (status == "SHIPPED") { /* 处理收货 */ }
- 每加一个状态,要修改这段代码
- 状态切换逻辑和行为混在一起,难维护
状态模式的好处:
- 把每个状态的逻辑单独放在一个类中
- 遵循 开闭原则(新增状态只加类,不改原来代码)
- 状态的切换由对象自身控制,不依赖外部复杂判断
两者对比,如下所示:
1.3、适用场景
在项目中常用的场景如下:
- Spring StateMachine(状态机框架)
- 订单系统:未支付 → 已支付 → 已发货 → 已完成
- 工作流引擎(Flowable、Activiti)
- Netty Channel 状态:UNREGISTERED → REGISTERED → ACTIVE → INACTIVE
💡 因此:
- 如果状态很少且不会变,可以用 if-else
- 如果状态多、变化频繁,并且有复杂行为 => 用状态模式
2、实现
2.1、if-else实现
public class Order {public static final int UNPAID = 0;public static final int PAID = 1;public static final int SHIPPED = 2;public static final int DONE = 3;private int status = UNPAID;public void process() {if (status == UNPAID) {System.out.println("当前订单未支付,执行支付逻辑...");status = PAID;} else if (status == PAID) {System.out.println("订单已支付,执行发货逻辑...");status = SHIPPED;} else if (status == SHIPPED) {System.out.println("订单已发货,执行收货逻辑...");status = DONE;} else if (status == DONE) {System.out.println("订单已完成");} else {System.out.println("未知订单状态");}}
}
测试:
public class Test {public static void main(String[] args) {Order order = new Order();order.process(); // 支付order.process(); // 发货order.process(); // 收货order.process(); // 已完成}
}
这种方式短期内可用,但问题比较明显:
1、集中式 if-else:
所有状态逻辑都在一个方法里面,方法不断膨胀,维护困难。
2、违反开闭原则:
新增状态时必须修改这个方法的代码,容易引入 bug。
3、状态切换逻辑分散:
可能在多个方法都有类似的 if-else,改动的时候要到处找。
4、可读性差:
状态和行为耦合得很紧,不直观。
2.2、状态模式实现
订单状态流转
1、定义状态接口
public interface OrderState {void handle(OrderContext context);
}
2、具体状态类
public class UnpaidState implements OrderState {public void handle(OrderContext context) {System.out.println("当前订单未支付,执行支付逻辑...");context.setState(new PaidState()); // 切换状态}
}public class PaidState implements OrderState {public void handle(OrderContext context) {System.out.println("订单已支付,执行发货逻辑...");context.setState(new ShippedState());}
}public class ShippedState implements OrderState {public void handle(OrderContext context) {System.out.println("订单已发货,执行收货逻辑...");context.setState(new DoneState());}
}public class DoneState implements OrderState {public void handle(OrderContext context) {System.out.println("订单已完成");}
}
3、环境类
public class OrderContext {private OrderState state;public OrderContext(OrderState state) { this.state = state; }public void setState(OrderState state) { this.state = state; }public void request() { state.handle(this); }
}
测试
public class Test {public static void main(String[] args) {OrderContext order = new OrderContext(new UnpaidState());order.request(); // 支付order.request(); // 发货order.request(); // 收货order.request(); // 已完成}
}输出:
当前订单未支付,执行支付逻辑...
订单已支付,执行发货逻辑...
订单已发货,执行收货逻辑...
订单已完成
基于前面的if-else和状态模式都需要多次去调用方法,因此可以对调用次数进行优化。
2.3、最终版
1、关于if-else的优化
public class Order {private int status = 0;private static final List<Integer> PROCESS_LIST = List.of(0, // UNPAID1, // PAID2, // SHIPPED3 // DONE);public void process() {if (status == 0) {System.out.println("未支付 -> 支付");status = 1;} else if (status == 1) {System.out.println("已支付 -> 发货");status = 2;} else if (status == 2) {System.out.println("已发货 -> 收货");status = 3;} else {System.out.println("订单完成");}}public static void main(String[] args) {Order order = new Order();PROCESS_LIST.forEach(s -> order.process());}
}
这样避免了写 4 行 order.process(),但 逻辑依旧集中在一个方法里,只是调用更“批量”了。
2、状态模式下的优化
状态模式下,我们可以更灵活地用集合来管理状态流转过程,而且不用去写 if-else。
例子:
interface State {void handle(OrderContext ctx);
}class UnpaidState implements State {public void handle(OrderContext ctx) {System.out.println("当前订单未支付 -> 执行支付逻辑...");ctx.setState(new PaidState());}
}class PaidState implements State {public void handle(OrderContext ctx) {System.out.println("订单已支付 -> 执行发货逻辑...");ctx.setState(new ShippedState());}
}class ShippedState implements State {public void handle(OrderContext ctx) {System.out.println("订单已发货 -> 执行收货逻辑...");ctx.setState(new DoneState());}
}class DoneState implements State {public void handle(OrderContext ctx) {System.out.println("订单已完成");}
}class OrderContext {private State state;public OrderContext(State state) {this.state = state;}public void setState(State state) {this.state = state;}public void process() {state.handle(this);}
}
驱动代码:用集合 + 循环执行
public class TestOrder {public static void main(String[] args) {OrderContext order = new OrderContext(new UnpaidState());// 一直循环直到状态是 DoneStatewhile (!(order.state instanceof DoneState)) {order.process();}// 最后一遍处理完成状态order.process();}
}
特点
- 不用写多行 request(),直接用循环驱动
- 如果状态机变长/变短,不影响调用逻辑
- 也可以设计一个 List 状态链 提前加载好,遍历一次性跑完
3、ArrayList 配置“状态流”
状态模式也可以结合 状态数组:
List<State> flow = List.of(new UnpaidState(),new PaidState(),new ShippedState(),new DoneState()
);OrderContext ctx = new OrderContext(flow.get(0));
for (State state : flow) {ctx.setState(state);ctx.process();
}
- 优势:业务状态流程可以从配置文件甚至数据库加载,不用改代码
- 实现可拥抱灵活性,比如读取“审批流”、“工单流”这些可配置流程
3、总结
如下所示:
总结
状态模式是一种行为型设计模式,允许对象在内部状态改变时改变其行为。模式中包含上下文、抽象状态和具体状态角色。
优点包括解耦客户端和状态对象,可扩展性强,避免大量条件语句。缺点是可能增加系统类的数量和复杂性。适用场景如自动售货机的状态转换、线程状态管理等。
参考文章:
1、设计模式第21讲——状态模式(State)-CSDN博客文章浏览阅读6.7k次,点赞22次,收藏104次。状态模式是一种行为型设计模式,允许对象在内部状态改变时改变其行为。模式中包含上下文、抽象状态和具体状态角色。优点包括解耦客户端和状态对象,可扩展性强,避免大量条件语句。缺点是可能增加系统类的数量和复杂性。适用场景如自动售货机的状态转换、线程状态管理等。代码示例展示了自动售卖机如何利用状态模式实现不同状态的切换。https://blog.csdn.net/weixin_45433817/article/details/131521862?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522d157caaef17e4af5a1ebe4b41acb1286%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=d157caaef17e4af5a1ebe4b41acb1286&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-131521862-null-null.142^v102^control&utm_term=%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4187