状态模式和策略模式有什么区别?
简化版
状态模式和策略模式结构很像,都是把行为委托给一个接口实现类。区别在于:策略模式是客户端主动选择一种算法,通常策略之间彼此独立;状态模式是对象内部状态变化导致行为变化,状态之间往往存在迁移关系。
详细版
两者的相似点是都使用组合和多态,把变化的行为封装到独立类中。
区别主要有三点:
- 意图不同:策略模式解决算法可替换,状态模式解决状态驱动行为变化;
- 控制方不同:策略通常由客户端或配置选择,状态通常由 Context 内部状态迁移决定;
- 关系不同:策略之间一般相互独立,状态之间通常有合法迁移关系。
比如支付方式选择微信、支付宝、银行卡,这是策略模式;订单从待支付到已支付、已发货、已完成,这是状态模式。
面试时可以说:如果关注“选哪个算法”,更像策略;如果关注“当前状态决定能做什么,以及下一步迁移到哪里”,更像状态模式。
完整版教学
一、为什么两者容易混淆
策略模式和状态模式代码结构非常像:
context.setXxx(interfaceImpl);
context.operation();
它们都把行为封装成接口实现类,都依赖多态执行不同逻辑。
所以只看类图,很难区分。真正要看的是设计意图。
二、策略模式关注“算法选择”
策略模式里,多个策略通常是平级替换关系。
比如折扣计算:
满减策略
折扣策略
会员价策略
新人券策略
一次计算选择其中一种或几种策略。策略之间通常不会说“满减策略执行完后自动切换成折扣策略”。
策略模式重点是让算法可替换。
三、状态模式关注“状态驱动行为”
状态模式中,状态不是随便选的,而是对象生命周期的一部分。
比如订单状态:
待支付 -> 已支付 -> 已发货 -> 已完成
待支付 -> 已取消
已支付 -> 已退款
每个状态能执行哪些操作,执行后迁移到哪个状态,都有业务规则。
状态模式重点是让对象在不同状态下表现出不同行为。
四、控制权不同
策略模式通常由外部决定用哪个策略:
calculator.setStrategy(new VipDiscountStrategy());
状态模式通常由对象内部状态推进:
order.pay(); // 内部从 WaitPayState 迁移到 PaidState
客户端不应该随意把订单从“待支付”直接改成“已完成”。状态迁移要受业务规则约束。
五、策略之间独立,状态之间有关联
策略模式中的策略一般彼此独立。
今天用 A 策略,明天用 B 策略,不代表 A 和 B 之间有生命周期关系。
状态模式中的状态通常有关联:
- 哪些状态可以迁移;
- 哪些迁移非法;
- 迁移时要做什么动作;
- 迁移失败如何回滚。
这就是状态模式比策略模式更强调“状态图”的原因。
六、它们可以组合使用
真实项目中状态模式和策略模式可以同时出现。
比如订单处于“已支付”状态时,要计算发货策略:
- 普通仓发货;
- 前置仓发货;
- 跨境仓发货。
订单状态决定当前能不能发货,这是状态模式;发货算法如何选择,这是策略模式。
七、外部选择算法与内部生命周期迁移
200 元订单可任选八折或满减两个策略;同一订单从 Pending→Paid→Shipped 则是有方向的 3 个状态。
strategy: selector -> algorithm; state: current --event--> next
状态题的正确性落在“当前状态 + 事件 + 守卫条件 → 目标状态”这一条边上,而不是类数量上。每条边都要说明持久化原子性、重复事件结果和副作用触发时机,非法边则必须得到稳定且可观测的拒绝。
八、迁移一致性、幂等与并发验证
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 一个状态内部仍可选择支付或运费策略,两种模式能够组合。 |
| 适用边界 | 策略通常彼此独立并由外部选择,状态之间有合法边并由事件推动;二者代码结构相似但意图不同。 |
| 测试证据 | 用状态×事件矩阵覆盖全部合法边与非法边,并验证两类结果都符合统一错误契约 |
| 工程代价 | 重点评估状态数、迁移边数、一次迁移的数据库竞争,以及新增状态对完整迁移矩阵的影响 |
易错点:一个状态内部仍可选择支付或运费策略,两种模式能够组合。
九、常见误区与追问
- 误区:状态模式只是自动选择策略。 状态承载生命周期语义和迁移约束,不只是选择来源不同。
- 误区:把状态码换成状态类就自动消除了所有条件判断。 状态对象只能封装状态相关行为;持久化、并发控制、权限和跨聚合规则仍需显式设计。
- 追问:怎样从需求语言快速判断? 出现“选择哪种算法”偏策略,出现“当前阶段允许什么、下一步到哪”偏状态。
- 追问:非法状态迁移应该静默忽略吗? 不应该;应返回明确业务错误并记录当前状态、事件和聚合标识,重复事件则按幂等规则单独处理。
- 追问:状态模式最容易漏掉哪类测试? 非法迁移、重复事件和并发竞争;它们比单线程成功路径更能证明状态模型可靠。
十、加强记忆
状态模式和策略模式都用多态封装行为,但关注点不同。策略模式问“这次选哪个算法”,状态模式问“当前状态下能做什么,做完迁移到哪里”。策略通常外部选择、彼此独立;状态通常内部推进、存在生命周期和合法迁移关系。