0.参考资料



1.概述

  • 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法的变化(扩展,子类化)可独立于客户程序(稳定)。 ——《设计模式》GoF
  • 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)

1.1动机

  1. - 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
  2. - 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题.

1.2结构

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629352459857-9e0fd319-caf9-4a81-9690-a7ef0046ece3.png#clientId=ud4e8fd8a-64d6-4&from=paste&height=259&id=u2f45fffe&margin=%5Bobject%20Object%5D&name=image.png&originHeight=517&originWidth=1395&originalType=binary&ratio=1&size=167148&status=done&style=none&taskId=u81badea8-ed30-4b09-a22d-929a6ab8134&width=697.5)
  2. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629366541757-6bc1780a-97f3-47e0-b2db-62beb219cea2.png#clientId=ud4e8fd8a-64d6-4&from=paste&height=314&id=udea6749d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=418&originWidth=812&originalType=binary&ratio=1&size=62631&status=done&style=none&taskId=udb15e15c-7e00-4968-a7f2-09c6e0e9cc4&width=609)

2.要点总结

宏观架构

  1. 1. Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换
  2. 1. Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式
  3. 1. 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。

微观代码

  1. 1. 策略模式的关键是:分析项目中变化部分与不变部分
  2. 1. 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
  3. 1. 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if..else if..else
  4. 1. 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
  5. 1. 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大

3.案例

需求

  1. - 有各种鸭子(比如 江西鸭、北京鸭等, 鸭子有各种行为,比如 叫、飞行等
  2. - 显示鸭子的信息

方案

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629366302763-13b6fb22-c7ed-4772-a6d0-5ac3f45b1c1d.png#clientId=ud4e8fd8a-64d6-4&from=paste&height=194&id=u7ca2add1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=387&originWidth=901&originalType=binary&ratio=1&size=47850&status=done&style=none&taskId=u3722d144-b59b-4a66-ac04-05e962c878e&width=450.5)

分析

  1. - 其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的
  2. - 上面说的1 的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。会有溢出效应
  3. - 为了改进1问题,我们可以通过覆盖fly 方法来解决 => 覆盖解决
  4. - 问题又来了,如果我们有一个玩具鸭子ToyDuck, 这样就需要ToyDuck去覆盖Duck的所有实现的方法 => 解决思路 策略模式 (strategy pattern)

4.使用模式

方案

  1. - 分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者

类图

  1. - ![SC(@CPW[YXV]]]YZ${AA(@V.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629371300291-06c27a24-023b-4f38-8073-a5923bc20515.png#clientId=ud4e8fd8a-64d6-4&from=paste&height=237&id=u439af13c&margin=%5Bobject%20Object%5D&name=SC%28%40CPW%5BYXV%5D%5D%5DYZ%24%7BAA%28%40V.png&originHeight=473&originWidth=1018&originalType=binary&ratio=1&size=36681&status=done&style=none&taskId=udd9249bd-9ccd-4111-835f-e17cc959501&width=509)
  2. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629372734534-a8825970-5eea-49e5-b7e1-40cb231c8e72.png#clientId=ud4e8fd8a-64d6-4&from=paste&height=242&id=u02d111ab&margin=%5Bobject%20Object%5D&name=image.png&originHeight=483&originWidth=1074&originalType=binary&ratio=1&size=243142&status=done&style=none&taskId=uec604908-9eb6-4334-8aa5-de2a7d32d5f&width=537)

代码

  1. - 策略类
  1. /**
  2. * @description: 飞翔的策略接口
  3. */
  4. public interface FlyStrategy {
  5. void fly(); // 子类具体实现
  6. }
  7. public class GoodFly implements FlyStrategy {
  8. @Override
  9. public void fly() {
  10. System.out.println("飞得很好");
  11. }
  12. }
  13. public class BadFly implements FlyStrategy {
  14. @Override
  15. public void fly() {
  16. System.out.println("非得很坏");
  17. }
  18. }
  19. public class NoFly implements FlyStrategy {
  20. @Override
  21. public void fly() {
  22. System.out.println("不会飞");
  23. }
  24. }
  1. - 调用端
  1. public abstract class Duck {
  2. // 把行为, 通过策略模式动态组合
  3. FlyStrategy flyStrategy;
  4. public void fly(){
  5. if (flyStrategy != null){
  6. flyStrategy.fly();
  7. }
  8. }
  9. public FlyStrategy getFlyStrategy() {
  10. return flyStrategy;
  11. }
  12. public void setFlyStrategy(FlyStrategy flyStrategy) {
  13. this.flyStrategy = flyStrategy;
  14. }
  15. }
  16. public class BeiJingDuck extends Duck {
  17. public BeiJingDuck(){
  18. flyStrategy = new BadFly();
  19. }
  20. }
  21. public class JiangXiDuck extends Duck {
  22. public JiangXiDuck(){
  23. flyStrategy = new GoodFly();
  24. }
  25. }
  1. - 测试及结果
  1. public class Client {
  2. public static void main(String[] args) {
  3. BeiJingDuck jingDuck = new BeiJingDuck();
  4. jingDuck.fly();
  5. JiangXiDuck duck = new JiangXiDuck();
  6. duck.fly();
  7. // 动态改变对象的行为
  8. duck.setFlyStrategy(new NoFly());
  9. duck.fly();
  10. }
  11. }
  1. 非得很坏
  2. 飞得很好
  3. 不会飞

5.经典使用


5.1JDK中Arrays.sort(… Comparator)

分析

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629372618235-543e5608-4c83-4249-ba6e-71945e248a1d.png#clientId=ud4e8fd8a-64d6-4&from=paste&height=377&id=u2ec19cd3&margin=%5Bobject%20Object%5D&name=image.png&originHeight=754&originWidth=1278&originalType=binary&ratio=1&size=613286&status=done&style=none&taskId=uf4031b35-bfcc-459d-b46d-629dafe98f4&width=639)
  2. - 其中, Comparator 就是策略类, new Comparator<Integer>{...} 即为具体的策略类(匿名方式)