策略模式
# 策略模式
定义:
该模式定义了一系列算法,并将每个算法封装起来;它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,他通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
结构:
抽象策略类「Strategy」:这是一个抽象角色,通常由一个接口或抽象类实现。此角色会给出说有的具体策略类所需的接口(抽取出抽象方法,起到规定的作用)
具体策略类「Concrete Strategy」:实现了抽象策略定义的接口,提供具体的算法实现或行为
环境类「Context」:出游一个策略类的引用,做种给客户端调用
策略模式其实就是在代码结构上调整,用接口+实现类+分派逻辑来使代码结构可维护性更好。
# 案例实现
【例子】促销活动
一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员将促销活动展示给客户。
类图如下:
Stretegy-抽象策略类:展示促销活动的内容
StretegyA~C-具体策略类:实现促销接口,负责展示该促销活动的内容
StretegyMan(销售员)-环境类:促销员为客户展示促销内容
Strategy接口
抽象策略类-提供接口, 促销活动接口
public interface Strategy {
void show();
}
2
3
StrategyA
具体策略类,封装算法;实现促销接口,负责展示该促销活动的内容
public class StrategyA implements Strategy {
@Override
public void show() {
System.out.println("买一送一");
}
}
2
3
4
5
6
public class StrategyB implements Strategy {
@Override
public void show() {
System.out.println("满200减50!");
}
}
2
3
4
5
6
public class StrategyC implements Strategy {
@Override
public void show() {
System.out.println("满1000打8折...");
}
}
2
3
4
5
6
SalesMan
环境类-聚合策略类;促销员
public class SalesMan {
// 聚合策略类
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
// 出校源展示促销活动给用户
public void salesManStrategy() {
strategy.show();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Usre用户测试类
public class Usre {
public static void main(String[] args) {
// 春节促销活动
SalesMan salesMan = new SalesMan(new StrategyA());
// 展示促销活动
System.out.print("春节促销活动!");
salesMan.salesManStrategy();
// 中秋促销活动
System.out.print("中秋促销活动!");
salesMan.setStrategy(new StrategyB());
salesMan.salesManStrategy();
// 元旦促销活动
System.out.print("元旦促销活动!");
salesMan.setStrategy(new StrategyC());
salesMan.salesManStrategy();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 策略模式优缺点
【优点】
易于拓展 增加一个新的策略只需要添加一个具体的策略即可,符合开闭原则
策略类之间可以自由切换 策略类都实现同一个接口,可以自由切换
优化多重条件选择语句 体现面向对象的设计思想
【缺点】
客户端必须知道所有的策略类,从而决定具体使用哪一个类
容易造成有很多策略类,可以通过<享元模式>在一定程度上减少对象数量
# 使用场景
- 一个系统需要动态的选择几种算法时,可以将每个算法的实现分装到策略类中;
- 一个类定义多种行为,可以将这个类的条件分支语句移入各自的策略类中代替条件语句
- 各算法完全独立,对客户隐藏具体算法实现的细节
# JDK中的策略模式应用
Comparator 中的策略模式。在Arrays类种有sort
方法如下:
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (c == null) {
sort(a, fromIndex, toIndex);
} else {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
2
3
4
5
6
7
8
9
10
11
12
Arrays 就是一个环境角色类,sort
方法传入 新策略 (由小到大还是由大到小)让Arrays根据策略进行排序。如下的测试类:
public static void main(String[] args) {
Integer[] nums = {4, 5, 1, 3, 9, 7, 5};
Arrays.sort(nums, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// [9, 7, 5, 5, 4, 3, 1]
}
2
3
4
5
6
7
8
9
10
在调用Arrays 类的sort
方法时,第二个参数传递的是 Comparator 接口的实现类对象。
Comparator 充当的是抽象策略角色
具体的实现类充当的是具体策略角色
Arrays 类充当环境角色类,持有抽象策略的引用来调用
Arrays 类的sort
方法到底有没有使用 Comparator 子类中的compare()
方法呢?在TimSort 类的sort
方法如下:
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,
Comparator<? super T> c) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
我们可以看到此处调用了
c
的compare方法,由此得出结论:Arrays 类的sort
方法使用了Comparator 子类中的compare()
方法