商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 如何用开闭原则优雅实现状态模式

如何用开闭原则优雅实现状态模式

  发布于2026-07-04 阅读(0)

扫一扫,手机访问

先抛出一个核心观点:将状态转换逻辑封装到每个状态类内部,新增状态时只需添加新类,无需修改现有状态类。这才是真正遵循开闭原则的状态模式实践。

传统状态模式实现中,状态间的转移逻辑往往散落在各个 evKey 方法里,新增一个状态就要反复修改多个已有状态类——这直接触碰了开闭原则(Open-Closed Principle, OCP)的红线:“对扩展开放,对修改关闭”。要想真正解耦、提升可维护性,关键在于把“何时切换”和“切换到哪个状态”这两项职责从上下文(SecretKeeper)中剥离出来,交给具体状态类自己决策。

那么,具体怎么改?

✅ 改进核心思路

  • SecretKeeper 不再直接调用 setState(new XxxState()),而是委托当前状态完成“输入处理 → 验证 → 决策跳转”全流程;
  • 每个 State 实现类负责:
    • 维护自身内部状态(如输入累积的密码);
    • 定义输入行为(setCode + doStuff);
    • 自主决定验证通过后应切换至的新状态(getSwitchedState()
  • SecretKeeper.evKey() 变为统一协调者:仅执行标准流程,不感知具体状态逻辑。

✅ 重构后的关键代码结构

public class SecretKeeper {
    private final int secretCode;
    private final String secret1, secret2;
    private State state;

    public SecretKeeper(int secretCode, String secret1, String secret2) {
        this.secretCode = secretCode;
        this.secret1 = secret1;
        this.secret2 = secret2;
        this.state = new LockedState(); // 初始状态
    }

    void printSecret1() { System.out.println(secret1); }
    void printSecret2() { System.out.println(secret2); }
    boolean checkCode(int code) { return code == secretCode; }

    void evKey(int digit) {
        state.setCode(digit);       // 输入处理
        state.doStuff();            // 执行当前状态行为(如打印、重置等)
        if (checkCode(state.getCode())) {
            state = state.getSwitchedState(); // ✅ 状态切换由当前状态决定
        }
    }
}

对应接口与状态实现:

public interface State {
    void setCode(int digit);
    int getCode();
    State getSwitchedState(); // 核心:切换目标由状态自己返回
    void doStuff();
}

public class LockedState implements State {
    private int code = 0;

    @Override
    public void setCode(int digit) {
        if (digit == 0) code = 0;
        else code = code * 10 + digit;
    }
    @Override
    public int getCode() { return code; }
    @Override
    public State getSwitchedState() {
        return new SemiLockedState(); // ✅ 新增 SemiLocked?只改此处!
    }
    @Override
    public void doStuff() {
        // 当前无副作用,仅累积输入
    }
}

public class SemiLockedState implements State {
    @Override
    public void setCode(int digit) { /* ... */ }
    @Override
    public int getCode() { return 0; }
    @Override
    public State getSwitchedState() {
        return new UnlockedState(); // 可灵活配置后续状态
    }
    @Override
    public void doStuff() {
        // 如:播放提示音、点亮指示灯等
    }
}

public class UnlockedState implements State {
    private int lastDigit;

    @Override
    public void setCode(int digit) { this.lastDigit = digit; }
    @Override
    public int getCode() { return lastDigit; }
    @Override
    public State getSwitchedState() {
        return new LockedState(); // 或返回其他状态(如 TimeoutState)
    }
    @Override
    public void doStuff() {
        switch (lastDigit) {
            case 1 -> printSecret1();
            case 2 -> printSecret2();
            case 0 -> {} // 保持 unlocked?或触发锁定?由业务定
            default -> {}
        }
    }
    // 注意:printSecret1/2 需通过 SecretKeeper 实例访问 —— 建议注入或回调
    // 实际中可将 SecretKeeper 作为参数传入 doStuff(),避免静态调用
}

⚠️ 注意事项与最佳实践

  • 避免静态方法依赖:原示例中 SecretKeeper.printSecret1() 是静态调用,破坏了封装性。推荐改为在 doStuff(StateContext context) 中传入上下文对象,或使用策略回调。
  • 状态不可变性(可选):若状态切换频繁且需线程安全,可让 getSwitchedState() 返回新实例,并确保 state 字段 final + volatile(配合同步)。
  • 状态生命周期管理:复杂场景下可引入状态工厂(StateFactory.getState(String name))或枚举驱动,进一步解耦创建逻辑。
  • 测试友好性:每个状态类可独立单元测试其 setCodegetCodegetSwitchedState 行为,无需启动整个系统。

✅ 总结

遵循开闭原则的状态模式,本质是将变化点封装为可插拔的组件。当需求要求新增 SemiLocked 状态时,你只需:

  1. 创建 SemiLockedState 类并实现 State 接口;
  2. LockedState.getSwitchedState() 中返回它;
  3. (可选)调整其他状态的跳转逻辑(如 UnlockedState 是否允许回到 SemiLocked)。

全程无需修改任何已有状态类的 evKey 或分支逻辑,也无需触碰 SecretKeeper 的核心流程——这才是真正可演进、易维护的面向对象设计。

本文转载于:https://www.php.cn/faq/2749969.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注