1. 什么是状态模式?

状态模式(State Pattern) 是一种行为型设计模式,允许对象在其内部状态改变时改变它的行为。这种模式通过将每个状态的行为封装到独立的类中,使得状态转换对客户端透明,同时避免了复杂的条件判断逻辑。

核心思想:将对象的状态抽象为独立的类,对象的行为由当前状态决定。当状态改变时,对象的行为也随之动态变化。

2. 为什么需要状态模式?

假设你正在开发一个电商订单系统,订单可能处于以下状态:

  • 待支付 → 已支付 → 已发货 → 已完成

  • 或可能进入已取消状态

传统实现可能用大量if-elseswitch判断状态:

if (state == "待支付") {
    // 处理支付逻辑
} else if (state == "已支付") {
    // 处理发货逻辑
} //...

问题

  • 代码臃肿,难以维护

  • 违反开闭原则(新增状态需要修改原有代码)

  • 状态转换逻辑分散在各处

3. 状态模式核心组件

3.1 角色定义

角色 职责
Context 维护当前状态对象,定义与状态相关的接口
State 抽象状态接口,声明状态对应的方法
ConcreteState 具体状态类,实现State接口,处理特定状态下的行为和状态转换逻辑

3.2 代码结构示例

3.2.1 定义状态接口
public interface OrderState {
    void pay(OrderContext context);     // 支付操作
    void ship(OrderContext context);    // 发货操作
    void cancel(OrderContext context);   // 取消操作
}
3.2.2 实现具体状态类

待支付状态

public class UnpaidState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("支付成功");
        context.setState(new PaidState());  // 状态流转
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("错误:尚未支付,不能发货");
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("订单已取消");
        context.setState(new CancelledState());
    }
}

已支付状态

public class PaidState implements OrderState {
    @Override
    public void pay(OrderContext context) {
        System.out.println("错误:订单已支付");
    }

    @Override
    public void ship(OrderContext context) {
        System.out.println("开始发货...");
        context.setState(new ShippedState());
    }

    @Override
    public void cancel(OrderContext context) {
        System.out.println("取消订单,退款处理中");
        context.setState(new CancelledState());
    }
}
3.2.3 上下文类(Context)
public 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 ship() {
        currentState.ship(this);
    }

    public void cancel() {
        currentState.cancel(this);
    }
}

4. 状态模式四大实现要点

4.1 状态转换的三种方式

  1. 由Context控制:上下文持有所有状态实例,根据条件切换

  2. 由具体状态类控制(推荐):每个状态知晓下一个合法状态

  3. 客户端控制(不推荐):破坏封装性

4.2 状态共享

当多个Context可能处于相同状态时,可以设计成享元模式共享状态实例:

public class StateFactory {
    private static Map<String, OrderState> states = new HashMap<>();
    
    static {
        states.put("unpaid", new UnpaidState());
        states.put("paid", new PaidState());
        //...
    }
    
    public static OrderState getState(String key) {
        return states.get(key);
    }
}

4.3 与策略模式的区别

  • 策略模式:算法族的选择,客户端主动选择策略

  • 状态模式:状态自动转换,行为随内部状态变化

4.4 线程安全考量

  • 如果Context在多线程环境下使用,需保证状态转换的原子性

  • 采用volatile修饰状态变量或使用锁机制

5. 实际应用场景

5.1 电商订单系统

  • 典型状态流转:待支付 → 已支付 → 已发货 → 已完成

  • 异常流程处理:超时取消、退款申请等

5.2 游戏角色状态

  • 角色行为随状态变化:正常、中毒、眩晕、无敌等状态

  • 状态可能叠加(需结合组合模式)

5.3 交通信号灯系统

  • 状态循环:红 → 绿 → 黄 → 红

  • 定时自动切换

6. 进阶架构实践

6.1 状态机引擎集成

结合状态模式实现可配置的状态机:

public class StateMachine {
    private Map<State, Map<Event, State>> transitions = new HashMap<>();
    private State currentState;

    public void addTransition(State source, Event event, State target) {
        transitions.computeIfAbsent(source, k -> new HashMap<>()).put(event, target);
    }

    public void handleEvent(Event event) {
        State nextState = transitions.get(currentState).get(event);
        if (nextState != null) {
            currentState.exit();
            currentState = nextState;
            currentState.enter();
        }
    }
}

6.2 与Spring框架整合

通过Spring管理状态Bean:

@Component
public class OrderStateMachine {
    @Autowired
    private Map<String, OrderState> states; // 自动注入所有实现
    
    private OrderState currentState;
    
    @PostConstruct
    public void init() {
        currentState = states.get("unpaidState");
    }
    
    public void changeState(String stateKey) {
        currentState = states.get(stateKey);
    }
}

6.3 状态持久化方案

  • 数据库存储当前状态标识

  • 使用枚举或状态码映射具体状态类

@Entity
public class Order {
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}

public enum OrderStatus {
    UNPAID, PAID, SHIPPED, CANCELLED
}

7. 状态模式优缺点分析

优势

  • 消除庞大的条件分支语句

  • 方便扩展新状态(符合开闭原则)

  • 状态转换逻辑集中管理

  • 提高代码可维护性和可测试性

局限

  • 类数量可能爆炸(每个状态一个类)

  • 不适用于简单状态场景(容易过度设计)

8. 最佳实践建议

  1. 明确状态边界:定义清晰的状态转换图

  2. 优先组合而非继承:通过组合状态对象实现灵活扩展

  3. 合理控制粒度:避免将每个微小差异都设计为独立状态

  4. 结合其他模式

    • 与观察者模式结合实现状态变更通知

    • 使用备忘录模式实现状态回滚

    • 配合责任链模式处理复杂状态转换

9. 典型面试问题

  1. 状态模式与策略模式的区别是什么?

  2. 如何处理多个Context实例共享相同状态?

  3. 如果状态转换需要依赖外部参数,如何设计?

  4. 如何实现状态变更的历史记录?

  5. 在分布式系统中如何保证状态一致性?

10. 总结

状态模式是处理复杂状态流转的利器,特别适合业务规则频繁变化的场景。通过将状态与行为绑定,使得系统更易维护和扩展。但需要注意避免过度设计,在状态数量较多且行为差异明显时采用本模式才能发挥最大价值。

适用场景判断标准:当发现一个操作包含大量条件语句,且这些条件依赖于对象的状态时,就是使用状态模式的良好时机。

延伸学习建议

  • 研究Spring State Machine框架实现

  • 了解状态模式在工作流引擎中的应用

  • 探索有限状态机(FSM)的理论基础

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐