代码界的川剧大师:状态模式的变脸艺术

35 阅读3分钟

代码界的川剧大师:状态模式的变脸艺术


一、当对象开始「精分」

你是否见过这样的戏精代码?
自动售货机收钱时是舔狗,出货时是高冷御姐;
游戏角色满血时勇如吕布,残血时怂似苟王;
电梯运行时六亲不认,故障时秒变贴心客服...

这就是状态模式的魔力——让对象像川剧演员般瞬间变脸,每个状态都是独立人格,却共享同一副躯壳!


二、影帝的表演手册(UML图)

   ┌─────────────┐          ┌─────────────┐
   │   Context   │<>───────>│   State     │
   ├─────────────┤          ├─────────────┤
   │ +request()  │          │ +handle()   │
   └──────△──────┘         └──────△──────┘
          │                        │
   ┌──────┴──────┐          ┌──────┴──────┐
   │  Concrete   │          │  Concrete   │
   │  Context    │          │   StateA    │
   └─────────────┘          └─────────────┘
  • 导演(Context):持有当前状态的戏精本体
  • 剧本大纲(State):定义所有表演套路
  • 分镜脚本(ConcreteState):具体人格的表演细节

三、程序界的奥斯卡现场(代码实战)

1. 创建戏精本体(自动售货机)
public class VendingMachine {
    private State state; // 当前人格
    private int stock = 5; // 库存

    // 初始化待机人格
    public VendingMachine() {
        this.state = new IdleState();
    }

    // 切换人格的方法
    void changeState(State state) {
        this.state = state;
    }

    // 以下是用户可能触发的操作
    public void insertCoin() {
        state.insertCoin(this);
    }

    public void selectItem() {
        state.selectItem(this);
    }

    public void dispense() {
        state.dispense(this);
        stock--;
    }
}
2. 编写表演剧本(状态接口)
interface State {
    void insertCoin(VendingMachine vm);
    void selectItem(VendingMachine vm);
    void dispense(VendingMachine vm);
}
3. 具体人格演绎
// 待机状态(高冷人设)
class IdleState implements State {
    public void insertCoin(VendingMachine vm) {
        System.out.println("💰 硬币已投入");
        vm.changeState(new HasCoinState());
    }
    
    public void selectItem(VendingMachine vm) {
        System.out.println("❌ 请先投币");
    }
    
    public void dispense(VendingMachine vm) {
        System.out.println("❌ 请先选择商品");
    }
}

// 已投币状态(舔狗模式)
class HasCoinState implements State {
    public void insertCoin(VendingMachine vm) {
        System.out.println("❌ 已投币,别塞了");
    }
    
    public void selectItem(VendingMachine vm) {
        System.out.println("✅ 商品已选择");
        vm.changeState(new DispensingState());
    }
    
    public void dispense(VendingMachine vm) {
        System.out.println("❌ 请先选择商品");
    }
}

// 出货状态(工作狂人格)
class DispensingState implements State {
    public void insertCoin(VendingMachine vm) {
        System.out.println("❌ 出货中请勿投币");
    }
    
    public void selectItem(VendingMachine vm) {
        System.out.println("❌ 出货中请勿选择");
    }
    
    public void dispense(VendingMachine vm) {
        if(vm.stock > 0) {
            System.out.println("🎉 商品掉落口");
            vm.changeState(new IdleState());
        } else {
            System.out.println("⚠️ 库存不足");
            vm.changeState(new SoldOutState());
        }
    }
}

四、状态模式 vs 策略模式:演员与军师的区别

维度状态模式策略模式
目的管理状态驱动的行为变化灵活替换算法
状态感知各状态知道下一个状态策略间无关联
自主权状态可自行切换由外部指定策略
典型应用订单状态流转支付方式选择
现实类比人格分裂锦囊妙计

五、代码影帝的片场实录

  1. 电商订单系统

    • 待付款 → 已付款 → 发货中 → 已完成
    • 每个状态限制不同操作(不能发货未付款订单)
  2. 游戏引擎

    • 角色状态:站立/奔跑/跳跃/受伤
    • NPC状态:巡逻/追击/攻击/逃跑
  3. 交通信号灯

    • 红灯停 → 绿灯行 → 黄灯减速
  4. 工单系统

    • 待处理 → 处理中 → 已解决 → 已关闭
  5. 播放器控制

    • 停止 → 播放 → 暂停 → 快进

冷知识
TCP协议的状态机(SYN-SENT、ESTABLISHED等)是状态模式的网络级应用!


六、防NG拍摄指南

  1. 避免上帝状态

    // 错误示范:在Context中写满if-else状态判断
    public void handle() {
        if (state == A) { ... }
        else if (state == B) { ... }
    }
    
  2. 状态转换规范化

    // 正确做法:把转换逻辑放在状态类中
    class StateA implements State {
        void handle(Context ctx) {
            // 处理逻辑
            ctx.changeState(new StateB());
        }
    }
    
  3. 警惕状态爆炸

    当状态超过10个时,考虑:
    1. 合并相似状态
    2. 引入状态层级
    3. 改用状态表驱动
    
  4. 共享状态对象

    // 无内部状态的状态类可以共享实例
    State idle = IdleState.INSTANCE;
    
  5. 异步状态处理

    // 复杂状态转换使用线程池处理
    executor.submit(() -> currentState.handleAsync());
    

七、奥斯卡颁奖典礼

状态模式让代码成为程序界的影帝:

  • :用于行为随状态变化的场景
  • :把状态转换逻辑封装在状态类中
  • 不要:让Context变成状态上帝
  • 不要:创建过多细碎状态类

当你在电商网站看到"订单已完成"时,请想起状态模式——那个在后台默默演绎了十几种状态变迁的无名影帝!