1. 策略模式概述
概念
- 定义了一系列算法,并将每一个算法封装起来,而且使它们还可以互相替换。策略模式让算法独立于使用它的客户而独立变化
特点
- 优点:策略模式扩展性和灵活性都相当不错。当有新的策略时,只需要增加一个策略类;要修改某个策略时,只需要更改具体的策略类,其他地方的代码都无需做任何调整
- 缺点:每 new 一个对象,相当于调用者多知道了一个类,增加了类与类之间的联系,不利于程序的松耦合
应用
- 通过策略模式我们可以为同一个需求选择不同的算法,以应对不同的场景(是否要求稳定性等),比如不同排序算法的选择
- 使用策略模式更好的做法是与工厂模式结合,将不同的策略对象封装到工厂类中,用户只需要传递不同的策略类型,然后从工厂中拿到对应的策略对象即可
- 策略模式还可以应用在图片缓存中,当我们开发一个图片缓存框架时,可以通过提供不同的策略类,让用户根据需要选择
缓存解码后的图片
、缓存未经解码的数据
或者不缓存任何内容
。在一些开源图片加载框架中,就采用了这种设计模式
Demo
定义排序算法接口
1
2
3interface ISort {
void sort(int[] arr);
}冒泡排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 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
19class 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
18class 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
18class 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
13public 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
5enum 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
30class 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
13public 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 类图都是一模一样的。两者都是采用一个变量来控制程序的行为
- 策略模式通过不同的策略执行不同的行为,状态模式通过不同的状态执行不同的行为
不同点
- 使用策略模式时,程序只需选择一种策略就可以完成某件事,每个策略类都是完整的,都能独立完成这件事,强调的是殊途同归
- 使用状态模式时,程序需要在不同的状态下不断切换才能完成某件事,每个状态类只能完成这件事的一部分,需要所有的状态类组合起来才能完整地完成这件事,强调的是随势而动