Update 设计模式 - 策略.md

对类图说明进行文字校对;在注意事项中补充策略对象的线程安全要求并精简代理模式对比;调整“与状态模式的比较”小节位置并补充文档链接。
This commit is contained in:
CyC2018
2026-10-30 20:19:45 +08:00
parent 04b1bf885a
commit 08623a691f
+11 -11
View File
@@ -6,21 +6,13 @@
策略模式可以让算法独立于使用它的客户端。
### 类图
- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法;
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,即可以在运行时更换 Context 使用的算法。
下图按后文的鸭子示例画出:Duck 是 Context,QuackBehavior 是 Strategy,quack()、performQuack() 和 setQuackBehavior() 分别对应 behavior()、doSomething() 和 setStrategy()。
<div align="center"> <img src="pics/dp-strategy.png" width="760px"/> </div><br>
### 与状态模式的比较
状态模式和策略模式的类图相似,都是通过组合一个接口对象来动态改变行为,区别在于这个对象是如何被切换的。
状态模式的 State 对象随运行过程中的状态转移而切换,转移由状态对象或 Context 的内部逻辑触发。例如订单处于“待支付”时调用 pay() 会完成支付,进入“已支付”后再调用 pay() 会被拒绝。
策略模式的 Strategy 对象由客户端创建并交给 Context,之后客户端通过 Context 使用它,需要时再替换为另一个策略。例如先为订单设置“标准快递”的运费计算策略,用户改选“加急”后替换为加急策略。
### 实现
设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。
@@ -221,12 +213,20 @@ public class FreightService {
- 调用方要先选出策略,通常由工厂或 Map 负责。
注意事项:
- 工厂和 Spring 中的策略对象只有一个,被所有请求共享,应当是无状态的:计算过程中的数据放在参数和局部变量里,不要存到成员变量中,否则并发请求会互相覆盖,见[Bean 的作用域与线程安全](Spring.md#bean-的作用域与线程安全);
- 策略接口只有一个方法、实现又很简单时,可以用 Lambda 代替实现类以减少类的数量,例如 `Map<String, IntUnaryOperator>` 的值写成 `kg -> 25 + (kg - 1) * 6`([附录 A1](#a1-运费策略与-if-else-对拍)中验证了这种写法);
- 模板方法通过继承固定算法的骨架,由子类覆写其中的步骤;策略通过组合替换整个算法;
- 责任链中的请求依次经过多个处理者,策略则只选择一个算法执行;
- 代理模式与策略模式的类图相似,都是接口加组合,但代理的目的是控制对真实对象的访问,策略的目的是替换算法;
- 用策略替代已有的 if-else 时,可以像[附录 A1](#a1-运费策略与-if-else-对拍)那样用同一组输入对比新旧实现的结果,确认一致后再切换。
### 与状态模式的比较
[状态模式](设计模式%20-%20状态.md)和策略模式的类图相似,都是通过组合一个接口对象来动态改变行为,区别在于这个对象是如何被切换的。
状态模式的 State 对象随运行过程中的状态转移而切换,转移由状态对象或 Context 的内部逻辑触发。例如订单处于“待支付”时调用 pay() 会完成支付,进入“已支付”后再调用 pay() 会被拒绝。
策略模式的 Strategy 对象由客户端创建并交给 Context,之后客户端通过 Context 使用它,需要时再替换为另一个策略。例如先为订单设置“标准快递”的运费计算策略,用户改选“加急”后替换为加急策略。
### JDK 中的应用
- java.util.Comparator#compare()
- java.util.concurrent.RejectedExecutionHandler#rejectedExecution():ThreadPoolExecutor 无法接收任务时调用它,AbortPolicy、CallerRunsPolicy、DiscardPolicy 和 DiscardOldestPolicy 是它的四种实现,通过构造方法或 setRejectedExecutionHandler() 设置。