
本文介绍如何基于开闭原则重构状态模式,使新增状态无需修改现有类,通过职责分离与策略化状态切换,提升系统可扩展性与维护性。
本文介绍如何基于开闭原则重构状态模式,使新增状态无需修改现有类,通过职责分离与策略化状态切换,提升系统可扩展性与维护性。
在经典的 State 模式实现中,若每个具体状态类(如 Locked、Unlocked)直接硬编码状态转移逻辑(例如 sk.setState(new Unlocked())),则每新增一个状态(如 SemiLocked),就必须修改所有可能跳转到它的原有状态类——这违反了开闭原则(Open-Closed Principle, OCP):即“对扩展开放,对修改关闭”。
要真正遵循 OCP,关键在于将状态转移决策从行为逻辑中解耦出来,让每个状态自身明确“满足什么条件时应切换到哪个新状态”,而非由调用方或其它状态类决定。以下是重构后的专业实践方案:
✅ 核心设计思想
- State 接口定义统一契约:setCode()、getCode()、doStuff() 和 getSwitchedState()(核心!)
- 状态转移完全由当前状态自主决定:getSwitchedState() 返回下一个状态实例,SecretKeeper 仅负责执行切换,不参与判断逻辑。
- SecretKeeper 不再持有具体状态类型,只依赖 State 接口,彻底消除对具体类的编译依赖。
✅ 重构后关键代码示例
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(); // 初始状态
}
public void evKey(int digit) {
state.setCode(digit);
state.doStuff();
if (checkCode(state.getCode())) {
state = state.getSwitchedState(); // ✅ 仅此处切换,逻辑完全委托给当前状态
}
}
private boolean checkCode(int code) {
return code == secretCode;
}
public void printSecret1() { System.out.println(secret1); }
public void printSecret2() { System.out.println(secret2); }
}
public interface State {
void setCode(int digit);
int getCode();
void doStuff();
State getSwitchedState(); // ? 决策权上移:由状态自己决定“下一步去哪”
}
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 void doStuff() { /* 可选:输入过程中的反馈,如闪烁LED */ }
@Override
public State getSwitchedState() {
// ✅ 新增 SemiLockedState?只需修改此处返回值,其余类完全不动!
return new SemiLockedState(); // 或 new UnlockedState() —— 策略自由配置
}
}
public class SemiLockedState implements State {
@Override
public void setCode(int digit) { /* ... */ }
@Override
public int getCode() { return 0; }
@Override
public void doStuff() { /* 如播放提示音 */ }
@Override
public State getSwitchedState() {
// 例如:输入'9'进入维护模式 → return new MaintenanceState();
return new UnlockedState();
}
}
⚠️ 注意事项与最佳实践
- 避免状态间强耦合:getSwitchedState() 应返回接口类型 State,而非具体类名,确保依赖倒置。
- 状态初始化与生命周期:建议使用工厂或依赖注入管理状态实例创建(尤其当状态需共享上下文时)。
- 线程安全考量:若 SecretKeeper 被多线程访问,state 字段需保证可见性(如 volatile)或采用不可变状态对象。
- 测试友好性:每个 State 实现可独立单元测试,验证其 getSwitchedState() 行为是否符合预期,无需启动整个系统。
✅ 总结:OCP 在状态模式中的落地,本质是将“状态如何变迁”这一变化点封装进每个状态内部,并通过接口契约统一暴露。新增状态只需实现 State 接口并正确实现 getSwitchedState(),零修改既有代码——这才是可演进、易维护的状态机设计。











