
本文介绍一种符合开闭原则的状态模式实现方式,通过将状态转换逻辑从上下文类中剥离并封装到各状态子类内部,使新增状态时无需修改现有代码,仅需添加新状态类即可。
本文介绍一种符合开闭原则的状态模式实现方式,通过将状态转换逻辑从上下文类中剥离并封装到各状态子类内部,使新增状态时无需修改现有代码,仅需添加新状态类即可。
在传统的状态模式实现中,状态间的流转逻辑(如“输入密码后跳转到 Unlocked”)往往分散在各个 State 实现类的 evKey() 方法中,直接调用 sk.setState(new XxxState())。这种设计违反了开闭原则(Open-Closed Principle, OCP):每新增一个状态(如 SemiLockedState),不仅需定义新类,还必须修改所有可能跳转到它的已有状态类(例如 LockedState 中需将 new Unlocked() 改为 new SemiLockedState()),导致高耦合与频繁变更。
理想的解决方案是:让每个状态类自主决定“满足条件后应切换为何种状态”,并将状态切换行为从上下文(SecretKeeper)中解耦出来。核心思路是:
- State 接口统一声明 getSwitchedState() 方法,由具体状态类自行返回下一个状态实例;
- 上下文 SecretKeeper 不再持有状态创建逻辑,仅负责协调:收集输入 → 触发状态处理 → 检查条件 → 调用 getSwitchedState() 完成切换;
- 状态内部专注自身数据维护(如 code 累积)、业务行为(doStuff())和转换策略(getSwitchedState()),彼此隔离。
以下是重构后的关键代码结构:
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(); // 当前状态下的业务动作(非转换逻辑)
}
以新增 SemiLockedState 为例,只需定义新类,无需改动 LockedState、UnlockedState 或 SecretKeeper:
public class SemiLockedState 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() {
// 可灵活配置:输入正确密码后进入 Unlocked,或继续 SemiLocked,甚至新增 FinalState
return new UnlockedState();
}
@Override
public void doStuff() {
// 半锁定状态下可限制部分功能,如仅允许输入但不响应打印指令
}
}
注意事项与最佳实践:
- getSwitchedState() 应返回新状态实例(而非复用单例),确保状态对象无共享状态;若需复用,可通过工厂或依赖注入管理;
- doStuff() 中避免调用 setState(),防止状态切换逻辑泄露;所有跳转必须经由 evKey() 统一入口;
- 若状态间存在复杂条件(如“连续输错3次进入 BlockedState”),可将计数器等上下文数据通过 SecretKeeper 传入状态方法,或引入 Context 参数对象解耦;
- 配合枚举或状态机配置表,可进一步支持运行时动态加载状态,适用于规则引擎场景。
这种设计真正实现了“对扩展开放,对修改关闭”:添加 SemiLockedState 仅需新增一个类并调整初始状态赋值,原有代码零修改,系统可维护性与可演进性显著提升。











