如何用开闭原则优雅实现状态模式
作者:BrightSoul
时间:2026-07-04
浏览:0
先抛出一个核心观点:将状态转换逻辑封装到每个状态类内部,新增状态时只需添加新类,无需修改现有状态类。这才是真正遵循开闭原则的状态模式实践。 传统状态模式实现中,状态间的转移逻辑往往散落在各个 evKey 方法里,新增一个状态就要反复修改多个已有状态类——这直接触碰了开闭原则(Open-Closed
先抛出一个核心观点:将状态转换逻辑封装到每个状态类内部,新增状态时只需添加新类,无需修改现有状态类。这才是真正遵循开闭原则的状态模式实践。
传统状态模式实现中,状态间的转移逻辑往往散落在各个 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))或枚举驱动,进一步解耦创建逻辑。 - 测试友好性:每个状态类可独立单元测试其
setCode、getCode和getSwitchedState行为,无需启动整个系统。
✅ 总结
遵循开闭原则的状态模式,本质是将变化点封装为可插拔的组件。当需求要求新增 SemiLocked 状态时,你只需:
- 创建
SemiLockedState类并实现State接口; - 在
LockedState.getSwitchedState()中返回它; - (可选)调整其他状态的跳转逻辑(如
UnlockedState是否允许回到 SemiLocked)。
全程无需修改任何已有状态类的 evKey 或分支逻辑,也无需触碰 SecretKeeper 的核心流程——这才是真正可演进、易维护的面向对象设计。
作者最新文章
索尼 Xperia 1 VIII / VII / VI 等手机获 Android 17 更新,新增桌面模式等功能
2026-09-08 16:44
加拿大留学监护声明书(IMM 5646)双页签署与公证核对指南
2026-09-03 15:02
在线PDF转图片教程:一键生成高清图片包
2026-09-03 12:04
Creo零基础入门:新建零件与第一次拉伸建模完整指南
2026-09-03 06:02
扫描件PDF转Word的在线操作步骤与编辑可行性判断
2026-09-02 18:39
热门文章
更多
精品专题
更多
Mac软件
更多
WINDOWS
更多


































