
本文介绍一种遵循开闭原则的状态模式实现方式:通过将状态转换逻辑从上下文类中剥离并封装到各状态类内部,使新增状态无需修改现有代码,仅需添加新状态类即可完成扩展。
本文介绍一种遵循开闭原则的状态模式实现方式:通过将状态转换逻辑从上下文类中剥离并封装到各状态类内部,使新增状态无需修改现有代码,仅需添加新状态类即可完成扩展。
在传统状态模式实现中(如原始代码所示),状态间的转换规则往往分散在各个 State 实现类的 evKey() 方法内,且直接调用 sk.setState(new XxxState())。这种设计违反了开闭原则(Open-Closed Principle):每新增一个状态(例如 SemiLockedState),就必须修改所有已有状态类中涉及转换逻辑的分支(如 Locked 中要支持跳转到 SemiLocked,Unlocked 中可能也要新增对应跳转),导致高耦合、低可维护性。
理想的解决方案是将“何时切换”与“切换到哪”解耦,并交由状态自身决定。核心思路如下:
- 上下文(SecretKeeper)不再感知具体状态类型,只负责统一调度:接收输入 → 更新当前状态的内部数据 → 触发状态行为 → 询问“是否应切换?切换为何种状态?”
- 每个 State 实现类完全封装其数据管理(如密码累积)、业务行为(如打印密钥)和转换策略(返回下一个状态实例)
- 状态转换由 getSwitchedState() 方法显式声明,而非硬编码 new XxxState(),便于未来配合工厂或策略动态生成
以下是重构后的关键代码结构:
public class SecretKeeper {
private int secretCode;
private 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(); // 或返回 LockedState,完全由该状态控制
}
@Override
public void doStuff() {
System.out.println("Semi-locked: awaiting confirmation...");
}
}✅ 优势总结:
- 符合开闭原则:添加 SemiLockedState 时,仅需新建类并实现 State 接口,无需修改 SecretKeeper、LockedState、UnlockedState 的任何代码;
- 职责清晰:每个状态类独立管理其数据、行为与流转逻辑;
- 易于测试:状态行为可单独单元测试,不依赖上下文生命周期;
- 支持组合与复用:getSwitchedState() 可返回工厂创建的实例、缓存单例,甚至基于条件返回不同状态,扩展性强。
⚠️ 注意事项:
- 避免在 getSwitchedState() 中引入复杂业务逻辑(如数据库查询),应保持轻量;若需外部依赖,可通过构造注入传递服务对象;
- 若状态间存在共享数据(如全局计数器),建议通过 SecretKeeper 提供只读访问接口,而非让状态直接持有上下文引用,防止循环依赖;
- 对于大型状态机,可进一步引入状态注册表(Map<String, Supplier<State>>)配合字符串驱动,实现配置化状态切换。
这种设计不仅解决了原始问题中的“修改扩散”痛点,更构建出真正可演进、易维护的状态系统骨架。

















