0%

设计模式之行为型模式(九):策略模式

1. 策略模式概述

  • 概念

    • 定义了一系列算法,并将每一个算法封装起来,而且使它们还可以互相替换。策略模式让算法独立于使用它的客户而独立变化
  • 特点

    • 优点:策略模式扩展性灵活性都相当不错。当有新的策略时,只需要增加一个策略类;要修改某个策略时,只需要更改具体的策略类,其他地方的代码都无需做任何调整
    • 缺点:每 new 一个对象,相当于调用者多知道了一个类,增加了类与类之间的联系,不利于程序的松耦合
  • 应用

    • 通过策略模式我们可以为同一个需求选择不同的算法,以应对不同的场景(是否要求稳定性等),比如不同排序算法的选择
    • 使用策略模式更好的做法是与工厂模式结合,将不同的策略对象封装到工厂类中,用户只需要传递不同的策略类型,然后从工厂中拿到对应的策略对象即可
    • 策略模式还可以应用在图片缓存中,当我们开发一个图片缓存框架时,可以通过提供不同的策略类,让用户根据需要选择缓存解码后的图片缓存未经解码的数据或者不缓存任何内容。在一些开源图片加载框架中,就采用了这种设计模式
  • Demo

    • 定义排序算法接口

      1
      2
      3
      interface ISort {
      void sort(int[] arr);
      }
    • 冒泡排序

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class BubbleSort implements ISort{
      @Override
      public void sort(int[] arr) {
      for (int i = 0; i < arr.length - 1; i++) {
      for (int j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
      // 如果左边的数大于右边的数,则交换,保证右边的数字最大
      arr[j + 1] = arr[j + 1] + arr[j];
      arr[j] = arr[j + 1] - arr[j];
      arr[j + 1] = arr[j + 1] - arr[j];
      }
      }
      }
      }
      }
    • 选择排序

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      class SelectionSort implements ISort {
      @Override
      public void sort(int[] arr) {
      int minIndex;
      for (int i = 0; i < arr.length - 1; i++) {
      minIndex = i;
      for (int j = i + 1; j < arr.length; j++) {
      if (arr[minIndex] > arr[j]) {
      // 记录最小值的下标
      minIndex = j;
      }
      }
      // 将最小元素交换至首位
      int temp = arr[i];
      arr[i] = arr[minIndex];
      arr[minIndex] = temp;
      }
      }
      }
    • 插入排序

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class InsertSort implements ISort {
      @Override
      public void sort(int[] arr) {
      // 从第二个数开始,往前插入数字
      for (int i = 1; i < arr.length; i++) {
      int currentNumber = arr[i];
      int j = i - 1;
      // 寻找插入位置的过程中,不断地将比 currentNumber 大的数字向后挪
      while (j >= 0 && currentNumber < arr[j]) {
      arr[j + 1] = arr[j];
      j--;
      }
      // 两种情况会跳出循环:1. 遇到一个小于或等于 currentNumber 的数字,跳出循环,currentNumber 就坐到它后面。
      // 2. 已经走到数列头部,仍然没有遇到小于或等于 currentNumber 的数字,也会跳出循环,此时 j 等于 -1,currentNumber 就坐到数列头部。
      arr[j + 1] = currentNumber;
      }
      }
      }
    • 环境类:将每种算法都作为一种策略封装起来

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class Sort implements ISort {

      private ISort sort;

      Sort(ISort sort) {
      this.sort = sort;
      }

      @Override
      public void sort(int[] arr) {
      sort.sort(arr);
      }

      // 客户端通过此方法设置不同的策略
      public void setSort(ISort sort) {
      this.sort = sort;
      }
      }
    • 客户端调用:setSort() 方法用来选择不同的排序策略

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class Client {
      @Test
      public void test() {
      int[] arr = new int[]{6, 1, 2, 3, 5, 4};
      Sort sort = new Sort(new BubbleSort());
      // 这里可以选择不同的策略完成排序
      // sort.setSort(new InsertSort());
      // sort.setSort(new SelectionSort());
      sort.sort(arr);
      // 输出 [1, 2, 3, 4, 5, 6]
      System.out.println(Arrays.toString(arr));
      }
      }

2. 策略模式与工厂模式结合 Demo

  • 创建排序策略枚举类

    1
    2
    3
    4
    5
    enum SortStrategy {
    BUBBLE_SORT,
    SELECTION_SORT,
    INSERT_SORT
    }
  • 在 Sort 类中使用简单工厂模式:将创建策略类的职责移到了 Sort 类中。客户端只需要和 Sort 类打交道,通过 SortStrategy 选择不同的排序策略即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class Sort implements ISort {

    private ISort sort;

    Sort(SortStrategy strategy) {
    setStrategy(strategy);
    }

    @Override
    public void sort(int[] arr) {
    sort.sort(arr);
    }

    // 客户端通过此方法设置不同的策略
    public void setStrategy(SortStrategy strategy) {
    switch (strategy) {
    case BUBBLE_SORT:
    sort = new BubbleSort();
    break;
    case SELECTION_SORT:
    sort = new SelectionSort();
    break;
    case INSERT_SORT:
    sort = new InsertSort();
    break;
    default:
    throw new IllegalArgumentException("There's no such strategy yet.");
    }
    }
    }
  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Client {
    @Test
    public void test() {
    int[] arr = new int[]{6, 1, 2, 3, 5, 4};
    Sort sort = new Sort(SortStrategy.BUBBLE_SORT);
    // 可以通过选择不同的策略完成排序
    // sort.setStrategy(SortStrategy.SELECTION_SORT);
    // sort.setStrategy(SortStrategy.INSERT_SORT);
    sort.sort(arr);
    // 输出 [1, 2, 3, 4, 5, 6]
    System.out.println(Arrays.toString(arr));
    }
    }

3. 策略模式和状态模式的异同

  • 相同点

    • 策略模式与状态模式非常类似,甚至他们的 UML 类图都是一模一样的。两者都是采用一个变量来控制程序的行为
    • 策略模式通过不同的策略执行不同的行为,状态模式通过不同的状态执行不同的行为
  • 不同点

    • 使用策略模式时,程序只需选择一种策略就可以完成某件事,每个策略类都是完整的,都能独立完成这件事,强调的是殊途同归
    • 使用状态模式时,程序需要在不同的状态下不断切换才能完成某件事,每个状态类只能完成这件事的一部分,需要所有的状态类组合起来才能完整地完成这件事,强调的是随势而动
-------------------- 本文结束感谢您的阅读 --------------------