笔记来源:尚硅谷Java设计模式(图解+框架源码剖析)

策略模式

1、鸭子问题

编写鸭子项目,具体要求如下:

  • 1)有各鸭子(比如野鸭、北京鸭、水鸭等,鸭子有各种行为,比如叫、飞行等)
  • 2)显示鸭子的信息

2、传统方案解决鸭子问题

UML 类图

image.png

核心代码

  1. public abstract class Duck {
  2. public void quark() {
  3. System.out.println("鸭子嘎嘎叫~");
  4. }
  5. public void swim() {
  6. System.out.println("鸭子哗哗游~");
  7. }
  8. public void fly() {
  9. System.out.println("鸭子腾腾飞~");
  10. }
  11. public abstract void display();
  12. }
  13. public class WildDuck extends Duck {
  14. @Override
  15. public void display() {
  16. System.out.println("野鸭子");
  17. }
  18. }
  19. public class PekingDuck extends Duck {
  20. @Override
  21. public void display() {
  22. System.out.println("北京鸭~");
  23. }
  24. @Override
  25. public void fly() {
  26. System.out.println("北京鸭不会飞~");
  27. }
  28. }
  29. public class ToyDuck extends Duck {
  30. @Override
  31. public void display() {
  32. System.out.println("玩具鸭~");
  33. }
  34. @Override
  35. public void quark() {
  36. System.out.println("玩具鸭不会叫~");
  37. }
  38. @Override
  39. public void swim() {
  40. System.out.println("玩具鸭不会游~");
  41. }
  42. @Override
  43. public void fly() {
  44. System.out.println("玩具鸭不会飞~");
  45. }
  46. }

传统的方式实现的问题分析和解决方案

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

3、策略模式基本介绍

  • 1)策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换。此模式让算法的变化独立于使用算法的客户
  • 2)这算法体现了几个设计原则
    • 第一、把变化的代码从不变的代码中分离出来
    • 第二、针对接口编程而不是具体类(定义了策略接口)
    • 第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)

原理类图

image.png

说明:从上图可以看到,客户Context有成员变量Strategy或者其他的策略接口。至于需要使用到哪个策略,可以在构造器中指定

4、策略模式解决鸭子问题

  • 1)应用实例要求:编写程序完成前面的鸭子项目,要求使用策略模式
  • 2)思路分析
    • 策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象
    • 原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者
  • 3)代码实现

UML 类图

image.png

image.png

核心代码

“叫”的行为

  1. /**
  2. * “叫”行为策略接口
  3. */
  4. public interface QuarkBehavior {
  5. void quark();
  6. }
  7. /**
  8. * “不会叫”行为策略对象
  9. */
  10. public class NoQuarkBehavior implements QuarkBehavior {
  11. @Override
  12. public void quark() {
  13. System.out.println("不会叫~");
  14. }
  15. }
  16. /**
  17. * “嘎嘎叫”行为策略对象
  18. */
  19. public class GagaQuarkBehavior implements QuarkBehavior {
  20. @Override
  21. public void quark() {
  22. System.out.println("嘎嘎叫~");
  23. }
  24. }
  25. /**
  26. * “咯咯叫”行为策略对象
  27. */
  28. public class GegeQuarkBehavior implements QuarkBehavior {
  29. @Override
  30. public void quark() {
  31. System.out.println("咯咯叫~");
  32. }
  33. }

“游泳”的行为

  1. /**
  2. * ”游泳“行为策略接口
  3. */
  4. public interface SwimBehavior {
  5. void swim();
  6. }
  7. /**
  8. * “不会游泳”行为策略对象
  9. */
  10. public class NoSwimHehavior implements SwimBehavior {
  11. @Override
  12. public void swim() {
  13. System.out.println("不会游泳~");
  14. }
  15. }
  16. /**
  17. * “会游泳”行为策略对象
  18. */
  19. public class CanSwimHehavior implements SwimBehavior {
  20. @Override
  21. public void swim() {
  22. System.out.println("会游泳~");
  23. }
  24. }

“飞”的行为

  1. /**
  2. * “飞行”行为策略接口
  3. */
  4. public interface FlyBehavior {
  5. void fly();
  6. }
  7. /**
  8. * “不会飞”行为策略对象
  9. */
  10. public class NoFlyBehavior implements FlyBehavior {
  11. @Override
  12. public void fly() {
  13. System.out.println("不会飞~");
  14. }
  15. }
  16. /**
  17. * “不太会飞”行为策略对象
  18. */
  19. public class BadFlyBehavior implements FlyBehavior {
  20. @Override
  21. public void fly() {
  22. System.out.println("不太会飞~");
  23. }
  24. }
  25. /**
  26. * “很会飞”行为策略对象
  27. */
  28. public class GoodFlyBehavior implements FlyBehavior {
  29. @Override
  30. public void fly() {
  31. System.out.println("很会飞~");
  32. }
  33. }

鸭子类

  1. /**
  2. * 抽象鸭子类
  3. */
  4. public abstract class Duck {
  5. protected QuarkBehavior quarkBehavior;
  6. protected SwimBehavior swimBehavior;
  7. protected FlyBehavior flyBehavior;
  8. public Duck() {
  9. display();
  10. }
  11. public void quark() {
  12. if (quarkBehavior != null) {
  13. quarkBehavior.quark();
  14. }
  15. }
  16. public void swim() {
  17. if (swimBehavior != null) {
  18. swimBehavior.swim();
  19. }
  20. }
  21. public void fly() {
  22. if (flyBehavior != null) {
  23. flyBehavior.fly();
  24. }
  25. }
  26. public void setQuarkBehavior(QuarkBehavior quarkBehavior) {
  27. this.quarkBehavior = quarkBehavior;
  28. }
  29. public void setSwimBehavior(SwimBehavior swimBehavior) {
  30. this.swimBehavior = swimBehavior;
  31. }
  32. public void setFlyBehavior(FlyBehavior flyBehavior) {
  33. this.flyBehavior = flyBehavior;
  34. }
  35. public abstract void display();
  36. }
  37. /**
  38. * 野鸭子
  39. */
  40. public class WildDuck extends Duck {
  41. public WildDuck() {
  42. super();
  43. quarkBehavior = new GegeQuarkBehavior();
  44. swimBehavior = new CanSwimHehavior();
  45. flyBehavior = new GoodFlyBehavior();
  46. }
  47. @Override
  48. public void display() {
  49. System.out.println("======野鸭子======");
  50. }
  51. }
  52. /**
  53. * 北京鸭
  54. */
  55. public class PekingDuck extends Duck {
  56. public PekingDuck() {
  57. super();
  58. quarkBehavior = new GagaQuarkBehavior();
  59. swimBehavior = new CanSwimHehavior();
  60. flyBehavior = new BadFlyBehavior();
  61. }
  62. @Override
  63. public void display() {
  64. System.out.println("======北京鸭======");
  65. }
  66. }
  67. /**
  68. * 玩具鸭
  69. */
  70. public class ToyDuck extends Duck {
  71. public ToyDuck() {
  72. super();
  73. quarkBehavior = new NoQuarkBehavior();
  74. swimBehavior = new NoSwimHehavior();
  75. flyBehavior = new NoFlyBehavior();
  76. }
  77. @Override
  78. public void display() {
  79. System.out.println("======玩具鸭======");
  80. }
  81. }

测试代码

  1. Duck wildDuck = new WildDuck();
  2. wildDuck.quark();
  3. wildDuck.swim();
  4. wildDuck.fly();
  5. Duck pekingDuck = new PekingDuck();
  6. pekingDuck.quark();
  7. pekingDuck.swim();
  8. pekingDuck.fly();
  9. System.out.println("===改变策略===");
  10. pekingDuck.setFlyBehavior(new NoFlyBehavior());
  11. pekingDuck.fly();
  12. Duck toyDuck = new ToyDuck();
  13. toyDuck.quark();
  14. toyDuck.swim();
  15. toyDuck.fly();

测试结果

  1. //======野鸭子======
  2. //咯咯叫~
  3. //会游泳~
  4. //很会飞~
  5. //======北京鸭======
  6. //嘎嘎叫~
  7. //会游泳~
  8. //不太会飞~
  9. //===改变策略===
  10. //不会飞~
  11. //======玩具鸭======
  12. //不会叫~
  13. //不会游泳~
  14. //不会飞~

5、策略模式在 JDK-Arrays 应用的源码分析

JDK 的ArraysComparator就使用了策略模式

  • 匿名类对象new Comparator<Integer>() {}实现了Comparator接口(策略接口)
  • public int compare(Integer o1, Integer o2) {}指定具体的处理方式
  1. Comparator<Integer> comparator = new Comparator<Integer>() {
  2. @Override
  3. public int compare(Integer o1, Integer o2) {
  4. return o1 > o2 ? 1 : -1;
  5. }
  6. };
  7. // 方式1
  8. Arrays.sort(data, comparator);
  9. System.out.println(Arrays.toString(data));
  10. // [1, 2, 3, 4, 8, 9]
  11. //方式2
  12. Arrays.sort(data, (v1, v2) -> v1.compareTo(v2) > 0 ? -1 : 1);
  13. System.out.println(Arrays.toString(data));
  14. //[9, 8, 4, 3, 2, 1]

6、策略模式的注意事项和细节

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