策略模式(Strategy Pattern)是一种行为模式(Behavior Design Patters),它能帮助系统在实时状态下,根据不同的情况选择策略。
在大型系统设计中,开发人员往往会遇到一种需求:即根据不同的条件选择不同的执行方法或者算法。例如:根据链表的长度选择一种常用的排序算法将链表中的元素排序。又例如:根据数据的类型和大小,从常用的数据压缩算法(ZIP,GZIP等)中选择一个最有效的算法。这些例子都是根据实时数据,动态地选择一种行为。
在策略模式中,有三类参与对象。
图一 策略模式结构。
我们使用一个简单的排序用例来展示策略模式的用法。这个用例实现了冒泡排序和基数排序算法。这两种算法通过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);
}
}
在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);
}
}
...
}
本章介绍了策略模式的结构和使用方法。策略模式的优势在于,当调用者使用策略时,调用者无需关心具体方法实现的细节,他/她只需专注于如何选择策略(使用选择实现方法)。
注册用户登陆后可留言