类型:行为型模式

意图:定义一系列算法,不同算法策略可以相互替换,并且互不影响。

主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

使用场景:一个系统需要动态地在几种算法中选择一种。

设计模式系列文章目录

角色

  • 策略上下文角色:持有抽象策略角色的引用,访问策略的入口
  • 抽象策略角色
  • 具体策略角色

UML

实战

以抽奖活动发奖为例,奖品多种多样,可能是现金奖,话费奖品,实物奖等等,每种奖品的发放方式都不一样,比如现金是直接转账,话费奖品是调用运营商提供接口发放,实物奖需要人工快递寄送。在未采用策略模式之前,少不了使用 if…else…来判断发放,当增加一种奖品类型时,就需要增加 if 判断。而采取策略模式之后,只需实现一个策略类即可,对原来的逻辑无需做任何改动,也不会影响其他策略的正常逻辑。

本文示例UML图

定义抽象策略接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @author WeJan
* @since 2020-02-06
*/
public interface PrizeSendStrategy {
String DEFAULT = "default";
String MONEY = "money";
String IN_KIND = "in_kind";
String CALL_CHARGE = "call_charge";

String type();

void doSend();
}

实现具体的策略

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
@Component
public class CallChargePrizeSendStrategy implements PrizeSendStrategy {
@Override
public String type() {
return CALL_CHARGE;
}

@Override
public void doSend() {
System.out.println("发放话费奖品");
}
}

@Component
public class MoneyPrizeSendStrategy implements PrizeSendStrategy {
@Override
public String type() {
return MONEY;
}

@Override
public void doSend() {
System.out.println("发放现金奖品");
}
}

@Component
public class InKindPrizeSendStrategy implements PrizeSendStrategy {
@Override
public String type() {
return IN_KIND;
}

@Override
public void doSend() {
System.out.println("发放实物奖品");
}
}

@Component
public class EmptyPrizeSendStrategy implements PrizeSendStrategy {
@Override
public String type() {
return DEFAULT;
}

@Override
public void doSend() {
System.out.println("不发奖");
}
}

实现对抽象策略封装的上下文对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author WeJan
* @since 2020-02-06
*/
public class PrizeSendContext {
private PrizeSendStrategy prizeSendStrategy;
public PrizeSendContext() {
}

public void setPrizeSendStrategy(PrizeSendStrategy prizeSendStrategy) {
this.prizeSendStrategy = prizeSendStrategy;
}

public void executePrizeSendStrategy() {
prizeSendStrategy.doSend();
}
}

抽取策略工厂

客户端需要判断要使用哪一个具体的策略类,若还是按照传统的方法 if…else…来判断策略模式就没有意义了,因此策略模式一般都是结合其他模式共同使用。本文中策略类使用 String 来标识,也可以在策略类中增加抽象方法,返回值为枚举类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class PrizeSendStrategyFactory implements ApplicationContextAware {
private static final Map<String, PrizeSendStrategy> PRIZE_SEND_STRATEGY_MAP = new HashMap<>();

public static PrizeSendStrategy getPrizeSendStrategy(String strategyKey) {
return PRIZE_SEND_STRATEGY_MAP.getOrDefault(strategyKey, PRIZE_SEND_STRATEGY_MAP.get(DEFAULT));
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, PrizeSendStrategy> beans = applicationContext.getBeansOfType(PrizeSendStrategy.class);
beans.values().forEach(bean -> PRIZE_SEND_STRATEGY_MAP.put(bean.type(), bean));
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author WeJan
* @since 2020-02-06
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class StrategyTest {

@Test
public void test() {
PrizeSendContext sendContext = new PrizeSendContext();
sendContext.setPrizeSendStrategy(PrizeSendStrategyFactory.getPrizeSendStrategy("money"));
sendContext.executePrizeSendStrategy();
}

}

输出结果为:

1
发放现金奖品

示例代码

参考