0%

设计模式-策略模式

策略模式(Strategy pattern),属于行为型模式,对象有某种行为,但在不同的场景中,该行为有不同的算法。

基本实现

使用Java代码实现基本功能

结构


涉及到了三个角色,Context(上下文)、Strategy(策略)、ConcreteStrategy(具体策略)

上下文

Context,用来操作策略的上下文,封装对象行为,示例如下

1
2
3
4
5
6
7
8
9
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}

策略

Strategy,接口,规定抽象行为,示例如下

1
2
3
public interface Strategy {
public int doOperation(int num1, int num2);
}

具体策略

ConcreteStrategy,实现策略,定义以下两个实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}

public class OperationSubstract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}

客户端

对如上代码测试,示例如下

1
2
3
4
5
6
7
8
public class Client {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println(context.executeStrategy(7, 2));
context = new Context(new OperationSubstract());
System.out.println(context.executeStrategy(7, 2));
}
}

output:

1
2
9
5

JDK简化

使用匿名内部类可以省去具体策略,JDK8中的Lambda表达式可以可以很好的代替匿名内部类,从而有效减少代码冗余

实现

使用JDK8中的Lambda表达式,删除具体策略OperationAddOperationSubstract,在客户端测试

客户端

在客户端实现具体策略,示例如下

1
2
3
4
5
6
7
8
public class Client {
public static void main(String[] args) {
Context context = new Context((int num1, int num2) -> num1 + num2);
System.out.println(context.executeStrategy(7, 2));
context = new Context((int num1, int num2) -> num1 - num2);
System.out.println(context.executeStrategy(7, 2));
}
}

output:

1
2
9
5

Spring使用

根据Spring的依赖注入特性,可以通过修改配置文件(XML-based或Java-based),改变在客户端使用的策略

实现

策略接口和具体实现类不变,修改上下文,增加配置文件,进行测试

上下文

将上下文注入Spring,示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class Context {
@Autowired
private Strategy strategy;

private int num1;
private int num2;

public void setNum(int num1 ,int num2) {
this.num1 = num1;
this.num2 = num2;
}

public int executeStrategy() {
return strategy.doOperation(num1, num2);
}
}

配置文件(Java-based)

选择需要在客户端使用的策略,注入Spring,示例如下

1
2
3
4
5
6
7
8
@Configuration
@ComponentScan("com.springStrategy")
public class Config {
@Bean
public Strategy strategy() {
return new OperationAdd(); //在此选择具体策略
}
}

客户端

对如上代码测试,示例如下

1
2
3
4
5
6
7
8
public class Client {
public static void main (String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
Context context = (Context)applicationContext.getBean("context");
context.setNum(7,2);
System.out.println(context.doOperation());
}
}

output:

1
9

替换大量if语句

当代码中存在大量的if,else语句时,就可以考虑使用策略模式重构了

重构前

if语句复杂且难以维护,而且可能会变得更大更复杂

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String execute(String policy){
if("i".equals(policy)){
return "身份证";
}
if("n".equals(policy)){
return "姓名";
}
if("c".equals(policy)){
return "银行卡";
}
if("m".equals(policy)){
return "手机号";
}
return "其他";
}

重构

用简单和易于维护得代码实现

策略

1
2
3
public interface Strategy {
String doOperation(String policy);
}

具体策略

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
public class IdcardStrategy implements Strategy{
@Override
public String doOperation(String policy) {
return "身份证";
}
}

public class NameStrategy implements Strategy{
@Override
public String doOperation(String policy) {
return "姓名";
}
}

public class MobileStrategy implements Strategy{
@Override
public String doOperation(String policy) {
return "手机号";
}
}

public class CardStrategy implements Strategy{
@Override
public String doOperation(String policy) {
return "银行卡";
}
}

public class OtherStrategy implements Strategy {
@Override
public String doOperation(String policy) {
return "其他";
}
}

工厂

使用工厂类帮助实现

1
2
3
4
5
6
7
8
9
10
11
12
public class StrategyFactory {
private static Map<String , Strategy> strategyMap = new HashMap<>();
static{
strategyMap.put("i", new IdcardStrategy());
strategyMap.put("n", new NameStrategy());
strategyMap.put("c", new CardStrategy());
strategyMap.put("m", new MobileStrategy());
}
public Optional<Strategy> creator(String type){
return Optional.ofNullable(strategyMap.get(type));
}
}

上下文

此时可以将代码改为

1
2
3
4
public String doOperation(String policy){
Strategy strategy = new StrategyFactory().creator(policy).orElse(new OtherStrategy());
return strategy.doOperation(policy);
}

总结

  • 策略模式不需要if,else语句,便于维护
  • 良好的扩展性,且新增具体策略时,不需要修改上下文和抽象策略
  • 客户端需要知道所有的具体策略,来自己决定使用哪个策略

完整代码已上传,项目地址https://github.com/yuan0824/DesignPattern

参考资料