Java设计模式-状态模式
模式概述
状态模式简介
核心思想:允许对象在内部状态改变时改变其行为,使对象看起来好像修改了其类。本质是将状态相关的行为封装到独立的状态类中,通过状态切换动态改变对象的行为逻辑。
模式类型:行为型设计模式(关注对象间的交互与职责分配)。
作用:
- 将状态相关的行为局部化,每个状态类独立管理自身行为,避免代码冗余;
- 使状态转换显式化,通过状态对象间的协作替代复杂的条件判断;
- 符合开闭原则,新增状态只需添加新状态类,无需修改现有代码。
典型应用场景:
- 订单状态流转(待支付→已支付→已发货→已完成→售后中);
- 交通灯状态切换(红灯→绿灯→黄灯);
- 工作流引擎中的步骤处理(草稿→审核中→已发布→已归档);
- 游戏角色状态(正常→中毒→隐身→死亡)。
我认为:状态模式是“行为的动态切换器”,通过将每个状态的行为独立成类,让对象在不同状态下像“换了一个人”一样工作,彻底告别冗长的if-else地狱。
课程目标
- 理解状态模式的核心思想和经典应用场景
- 识别应用场景,使用状态模式解决功能要求
- 了解状态模式的优缺点
核心组件
角色-职责表
角色 | 职责 | 示例类名 |
---|---|---|
抽象状态(State) | 定义所有具体状态的公共接口,声明该状态下需要处理的行为方法 | OrderState |
具体状态(Concrete State) | 实现抽象状态接口,封装该状态下的具体行为逻辑,负责状态转换(可选) | UnpaidState 、PaidState |
上下文(Context) | 持有当前状态对象,将外部请求委托给当前状态处理;提供状态切换方法 | OrderContext |
类图
下面是一个简化的类图表示,展示了状态模式中的主要角色及其交互方式:
传统实现 VS 状态模式
案例需求
案例背景:实现一个订单系统,订单有“待支付”“已支付”“已发货”三种状态,不同状态下执行不同操作(如支付、发货、确认收货)。
传统实现(痛点版)
代码实现:
// 传统写法:上下文类直接包含状态判断逻辑
public class Order {
private String state; // 状态:"未支付" "已支付" "已发货" "已完成"
public void pay() {
if ("未支付".equals(state)) {
System.out.println("支付成功,订单状态变更为已支付");
state = "已支付";
} else {
System.out.println("当前状态无法支付");
}
}
public void deliver() {
if ("已支付".equals(state)) {
System.out.println("订单已发货,状态变更为已发货");
state = "已发货";
} else {
System.out.println("当前状态无法发货");
}
}
public void confirm() {
if ("已发货".equals(state)) {
System.out.println("确认收货,订单完成");
state = "已完成";
} else {
System.out.println("当前状态无法确认收货");
}
}
// 状态设置方法(省略getter/setter)
}
痛点总结:
- 代码臃肿:每个操作方法(pay/deliver/confirm)都需包含状态判断逻辑,状态越多代码越膨胀;
- 可维护性差:新增状态(如“退款中”)需修改所有相关方法,违反开闭原则;
- 状态耦合:状态转换逻辑分散在各方法中,难以统一管理和追踪;
- 复用性低:状态相关行为无法被其他对象复用(如另一个订单类型需要相同状态逻辑)。
状态模式 实现(优雅版)
代码实现:
// 1. 抽象状态接口
interface OrderState {
void pay(OrderContext context); // 支付操作
void deliver(OrderContext context);// 发货操作
void confirm(OrderContext context);// 确认收货
}
// 2. 具体状态类:未支付
class UnpaidState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("支付成功,订单状态变更为已支付");
context.setState(new PaidState()); // 切换状态
}
@Override
public void deliver(OrderContext context) {
System.out.println("当前状态未支付,无法发货");
}
@Override
public void confirm(OrderContext context) {
System.out.println("当前状态未支付,无法确认收货");
}
}
// 3. 具体状态类:已支付
class PaidState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("当前状态已支付,无需重复支付");
}
@Override
public void deliver(OrderContext context) {
System.out.println("订单已发货,状态变更为已发货");
context.setState(new DeliveredState()); // 切换状态
}
@Override
public void confirm(OrderContext context) {
System.out.println("当前状态已支付,未发货,无法确认收货");
}
}
// 4. 具体状态类:已发货(类似实现,省略部分代码)
class DeliveredState implements OrderState {
@Override
public void confirm(OrderContext context) {
System.out.println("确认收货,订单完成");
context.setState(new CompletedState());
}
// 其他方法实现...
}
// 5. 上下文类:订单上下文
class OrderContext {
private OrderState currentState;
public OrderContext() {
this.currentState = new UnpaidState(); // 初始状态为未支付
}
public void setState(OrderState state) {
this.currentState = state;
}
// 委托请求给当前状态处理
public void pay() {
currentState.pay(this);
}
public void deliver() {
currentState.deliver(this);
}
public void confirm() {
currentState.confirm(this);
}
}
// 使用示例
public class Client {
public static void main(String[] args) {
OrderContext order = new OrderContext();
order.pay(); // 输出:支付成功,订单状态变更为已支付
order.deliver(); // 输出:订单已发货,状态变更为已发货
order.confirm(); // 输出:确认收货,订单完成
}
}
优势:
- 高内聚低耦合:每个状态的行为封装在独立类中,修改或新增状态仅需调整对应类;
- 符合开闭原则:新增状态(如“退款中”)只需添加新的
RefundedState
类,无需修改现有代码; - 状态转换清晰:状态切换逻辑集中在状态类或上下文类中,易于追踪和管理;
- 行为复用性高:状态类可被多个上下文对象共享(如多个订单共享同一状态实例)。
局限:
- 类数量增加:若状态数量过多(如超过10种),会导致类膨胀,增加系统复杂度;
- 初始成本高:简单场景(如状态少且行为简单)下,状态模式可能过度设计;
- 状态共享需注意线程安全:若多个上下文共享同一状态实例,需考虑并发修改问题(可通过无状态状态类或同步机制解决)。
模式变体
共享状态(Flyweight State):当多个上下文需要共享同一状态实例时(如无状态状态类),可将状态对象池化,减少内存占用;
状态转换集中管理:将状态转换逻辑从状态类中抽离,由上下文或独立的“状态机”类统一管理(适合复杂状态转换规则);
枚举状态实现:利用Java枚举的特性,将每个状态定义为枚举值并实现状态接口(代码更简洁,适合轻量级场景);
enum OrderStateEnum implements OrderState { UNPAID { @Override public void pay(OrderContext context) { /* 实现 */ } }, PAID { @Override public void deliver(OrderContext context) { /* 实现 */ } }; }
分层状态模式:将状态按层级划分(如基础状态+扩展状态),减少重复代码(适合状态间有公共行为的场景)。
最佳实践
建议 | 理由 |
---|---|
状态数量≥3时优先使用 | 状态较少(如2种)时,传统条件判断更简单;状态≥3时,状态模式的优势显著 |
状态类尽量无状态 | 无状态的状态类可被所有上下文共享(单例),减少内存开销 |
状态转换逻辑统一管理 | 若转换规则复杂(如依赖上下文数据),建议在上下文类中集中处理,避免状态类过度耦合 |
避免状态持有上下文强引用 | 防止内存泄漏,可通过弱引用或让上下文传递自身(如方法参数传入this ) |
为状态接口定义完整契约 | 明确每个状态必须实现的方法,避免运行时出现未处理的行为分支 |
一句话总结
状态模式通过将状态相关的行为封装到独立的状态类中,使对象在不同状态下动态切换行为逻辑,彻底解决了传统实现中条件判断冗余、可维护性差的问题,是管理复杂状态流转的优雅解决方案。
如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊