strategy

第二十三章 策略模式(Strategy Pattern)

1 概要

策略模式(Strategy Pattern)是一种行为模式(Behavior Design Patters),它能帮助系统在实时状态下,根据不同的情况选择策略。

在大型系统设计中,开发人员往往会遇到一种需求:即根据不同的条件选择不同的执行方法或者算法。例如:根据链表的长度选择一种常用的排序算法将链表中的元素排序。又例如:根据数据的类型和大小,从常用的数据压缩算法(ZIP,GZIP等)中选择一个最有效的算法。这些例子都是根据实时数据,动态地选择一种行为。

2 策略模式的结构

在策略模式中,有三类参与对象。

  1. Strategy接口定义了策略的功能和实现的接口。
  2. ConcreteStrategy是具体策略的实现类。例如上述例子中的排序算法的实现或者数据压缩算法的实现。
  3. Context上下文类提供了策略选择所需要的信息。

图一 策略模式结构

图一 策略模式结构。

3 策略模式示例

我们使用一个简单的排序用例来展示策略模式的用法。这个用例实现了冒泡排序和基数排序算法。这两种算法通过SortContext调用。SortContext封装了待使用的排序算法的实现。因此,它将输入的数据与待使用的算法分离开。在实现排序算法时,开发人员只需专注于算法本身;而在调用算法时,开发人员只需专注于如何选择最好的算法。

这个策略模式的示例代码如下所示。我们首先定义一个排序算法的接口SortStrategy,并将冒泡排序和基数排序的内容分别放在BubbleSortStrategy和RadixSortStrategy中。为了更清晰的展示策略模式的结构,我们此处省略了排序算法实现的细节。排序算法的实现逻辑和示例代码可参见数据结构-排序

public interface SortStrategy {
    void sort(int[] data);
}

public class BubbleSortStrategy implements SortStrategy {
    void sort(int[] data) {
        // implement bubble sort here
        ...
    }
}

public class RadixSortStrategy implements SortStrategy {
    void sort(int[] data) {
        // implement radix sort here
        ...
    }
}

此处,SortContext仅仅只持有一个SortStrategy的变量引用。

public class SortContext {
    private SortStrategy strategy = null;
    public SortContext(SortStrategy strategy) {
        this.strategy = strategy;
    }
    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }
    public void sort(int[] data) {
        strategy.sort(data);
    }
}

在使用排序算法时,我们通过向SortContext传递一个具体的排序实例,然后调用SortContext.sort()方法进行真正的排序操作。

public class SortAgeTester {
    public static void main(String[] args) {
        // 给年龄排序
        int[] age = {26, 38, 14, 65, 18, 74, 56};
        SortContext ctx = new SortContext(new RadixSortStrategy());
        ctx.sort(age);

        // 给身高排序
        int[] height = {126, 138, 114, 165, 118, 74, 156, 202, 172};
        ctx.setStrategy(new BubbleSortStrategy());
        ctx.sort(height);
    }
}

4 应用示例

在Java标准库中,java.util.Comparator应用了策略模式。例如,在java.util.List接口中定义了成员方法sort()。它接受一个Comparator对象,用于比较两个对象之间的大小关系,最终完成整个链表的排序功能。之所以sort()方法接受Comparator对象,将对象之间的比较交由开发人员来指定,是因为对象比较的方法多种多样。按照不同的比较策略,能够得出不同的对象序例。例如,一个字符串链表可以根据字符串的字典序排列,也可以按照字符串长度排列。排序使用的策略由开发人员实时指定。

从List.java的源代码中可以看出,List.sort()先将链表结构转换为数组结构,然后再使用Arrays.sort()完成排序。排序完成后,再将数组转换回链表结构。

// OpenJDK 15
package java.util;

public interface List<E> extends Collection<E> {
    ...
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();  // 转换为数组结构
        Arrays.sort(a, (Comparator) c);  // 排序
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {  // 转换回链表结构
            i.next();
            i.set((E) e);
        }
    }
    ...
}

public class Arrays {
    ...
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
    ...
}

5 小结

本章介绍了策略模式的结构和使用方法。策略模式的优势在于,当调用者使用策略时,调用者无需关心具体方法实现的细节,他/她只需专注于如何选择策略(使用选择实现方法)。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.