GoF设计模式(二十二):Strategy Pattern 策略模式

我们知道对于一个工作而言,具体的实现途径、办法其实有很多种。那么就可以将这些实现途径、办法封装为一个个所谓的策略,在需要时选择适当的合适的即策略来完成该工作即可。这就是所谓的Strategy Pattern 策略模式,当然其同样是一种行为型的设计模式

abstract.jpeg

模式思想

这里就拿大家最熟悉的排序来说吧,我们知道排序算法有很多种,包括冒泡排序、插入排序、选择排序等等。它们都可以用于排序。但是对于不同场景下,各排序算法之间在效率上还是有明显的差异的。这个时候,就可以由client客户端自行选择当前场景下最合适的排序算法来进行排序。而这就是所谓的策略模式

在该模式下,其通常有以下几个角色

  • 环境角色:该角色进行了一定的封装,并在内部持有一个具体策略角色的实例用于完成实际的工作。即本文的数组排序工具ArrayUtil类
  • 抽象策略角色:其作为接口类,定义了各种不同策略的通用方法接口。即本文的排序算法SortAlgorithm类
  • 具体策略角色:其是对抽象策略角色的具体实现,即不同的策略是通过多个具体策略类来实现的。即本文的冒泡排序BubbleSort、选择排序SelectionSort、插入排序InsertionSort类

实现

下面,我们使用Java来实现一个基于策略模式的例子,以便更好的理解。首先定义一个环境角色——即数组排序工具ArrayUtil类

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
/**
* 环境角色: 数组排序工具
*/
public class ArrayUtil {
/**
* 排序算法
*/
private SortAlgorithm sortAlgorithm;

public void setSortAlgorithm(SortAlgorithm sortAlgorithm) {
this.sortAlgorithm = sortAlgorithm;
}

/**
* 对数组升序排列
* @param array
* @param <T>
* @return
*/
public <T extends Comparable> void sortedByAsc(T[] array) {
if(array==null || array.length==0) {
return;
}
sortAlgorithm.sort(array);
}

}

可以看到,其对外提供了一个对数组升序排序的方法。但是具体怎么实现排序呢?这就需要引入我们的各种排序算法了,即所谓的策略。为此需要一个抽象策略角色来定义各种排序算法的通用方法接口,即排序算法SortAlgorithm类

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 抽象策略角色: 排序算法
*/
public interface SortAlgorithm {
/**
* 对数组排序
* @param array
* @param <T>
* @apiNote 按升序排序
*/
<T extends Comparable> void sort(T[] array);
}

最后就很简单了,提供各种具体策略的实现即可,即实现各种排序算法

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* 具体策略角色: 冒泡排序
*/
public class BubbleSort implements SortAlgorithm {

@Override
public <T extends Comparable> void sort(T[] array) {
int size = array.length;
for( int i=0; i<size-1; i++ ) {
for(int j=0; j<size-i-1; j++) {
if( array[j].compareTo(array[j+1]) > 0 ) {
T temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}

}
...
/**
* 具体策略角色: 选择排序
*/
public class SelectionSort implements SortAlgorithm {

@Override
public <T extends Comparable> void sort(T[] array) {
int size = array.length;
for(int i=0; i<size-1; i++) {
int minIndex = i;
for(int j=i+1; j<size; j++) {
if( array[minIndex].compareTo(array[j])>0 ) {
minIndex = j;
}
}
if( minIndex != i ) {
T temp = array[i];
array[i] = array[minIndex];
array[minIndex] = temp;
}
}
}
}
...
/**
* 具体策略角色: 插入排序
*/
public class InsertionSort implements SortAlgorithm {
@Override
public <T extends Comparable> void sort(T[] array) {
int size = array.length;
for(int i=1; i<size; i++) {
T temp = array[i];
int j = i-1;
for( ; j>=0 && array[j].compareTo(temp)>0; j--) {
array[j+1] = array[j];
}
array[j+1] = temp;
}
}
}

至此,我们的策略模式就已经实现完毕了。现在来看看怎么运作起来

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
31
32
/**
* Strategy Pattern 策略模式 Demo
*/
public class StrategyPatternDemo {
public static void main(String[] args) {
ArrayUtil arrayUtil = new ArrayUtil();

System.out.println("------------------ Test 1 ------------------");
Integer[] array1 = {3,1,5,11,2,8};
System.out.println("Before sort: " + Arrays.toString(array1));
// client 自行指定策略, 这里使用冒泡排序
arrayUtil.setSortAlgorithm( new BubbleSort() );
arrayUtil.sortedByAsc( array1 );
System.out.println("After sort: " + Arrays.toString(array1));

System.out.println("\n------------------ Test 2 ------------------");
Double[] array2 = {11.0, -3.2, 1.1, -5.22, 2.73, -9.66};
System.out.println("Before sort: " + Arrays.toString(array2));
// client 自行指定策略, 这里使用选择排序
arrayUtil.setSortAlgorithm( new SelectionSort() );
arrayUtil.sortedByAsc( array2 );
System.out.println("After sort: " + Arrays.toString(array2));

System.out.println("\n------------------ Test 3 ------------------");
String[] array3 = {"Tim", "Bob", "Tony", "Aaron", "Tom"};
System.out.println("Before sort: " + Arrays.toString(array3));
// client 自行指定策略, 这里使用插入排序
arrayUtil.setSortAlgorithm( new InsertionSort() );
arrayUtil.sortedByAsc( array3 );
System.out.println("After sort: " + Arrays.toString(array3));
}
}

测试结果如下,符合预期

figure 1.png

最后再补充说明下,很多朋友会发现我们之前介绍状态模式看上去好像和策略模式很相似。其实不然,我们如果从client使用角度来看,会发现状态模式在进行状态切换时,client客户端是无感知的,即所谓的自动切换状态;而在策略模式下,策略的切换实际上是由client自行指定切换的

参考文献

  1. Head First 设计模式 弗里曼著
0%