在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

介绍

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

使用场景

  1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  2. 一个系统需要动态地在几种算法中选择一种。
  3. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

优缺点及注意

优点

  1. 算法可以自由切换。
  2. 避免使用多重条件判断。
  3. 扩展性良好。

缺点

  1. 策略类会增多。
  2. 所有策略类都需要对外暴露。

注意

如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

实现

策略角色

1
2
3
4
5
6
public interface Strategy {
/**
* 算法方法
*/
public void algorithmInterface();
}

以下三个实现类为具体的策略角色

1
2
3
4
5
6
public class ConcreteStrategyA  implements Strategy{
@Override
public void algorithmInterface() {
System.out.println("具体的策略A");
}
}
1
2
3
4
5
6
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
System.out.println("具体的策略B");
}
}
1
2
3
4
5
6
7
public class ConcreteStrategyC implements Strategy {
@Override
public void algorithmInterface() {
System.out.println("具体的策略C");
}
}

Context上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Context {
private Strategy strategy;

public Context(Strategy strategy) {
this.strategy = strategy;
}

/**
* 上下文接口,执行对应策略
*/
public void executeStrategy() {
strategy.algorithmInterface();
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {

public static void main(String[] args) {
Context context;

context = new Context(new ConcreteStrategyA());
context.executeStrategy();

context = new Context(new ConcreteStrategyB());
context.executeStrategy();

context = new Context(new ConcreteStrategyC());
context.executeStrategy();
}
}

结果:

1
2
3
具体的策略A
具体的策略B
具体的策略C

使用Spring实现策略模式+工厂模式

1、实现策略类

1
2
3
4
public interface Strategy {
//策略模式的运算法则
void doSomething();
}
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class ConcreteStrategy1 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略1的运算法则...");
}

@Override
public String toString() {
return "具体策略1";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class ConcreteStrategy2 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略2的运算法则...");
}

@Override
public String toString() {
return "具体策略2";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class DefaultStrategy implements Strategy {
@Override
public void doSomething() {
System.out.println("默认策略的运算法则...");
}

@Override
public String toString() {
return "默认策略";
}
}

2、实现工厂类

1
2
3
4
5
6
7
8
9
10
@Component
public class StrategyFactory {
//Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
@Autowired
private Map<String, Strategy> strategyMap;

public Strategy getBy(String strategyName) {
return strategyMap.get(strategyName);
}
}

Spring会自动将Strategy接口的实现类注入到这个Map中(前提是实现类得是交给Spring 容器管理的),这个Map的key为bean id,可以用@Component(value = “xxx”)的方式设置,如果直接用默认的方式的话,就是首字母小写。value值则为对应的策略实现类。

20210907163635

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
class SpringbootDemoApplicationTests {

@Autowired
private ApplicationContext context;

@Test
public void test() {
context.getBean(StrategyFactory.class).getBy("concreteStrategy1").doSomething();
context.getBean(StrategyFactory.class).getBy("concreteStrategy2").doSomething();
}

}

执行结果如下:

1
2
具体策略1的运算法则...
具体策略2的运算法则...

3、别名转换

上面测试类调用的使用使用的bean id,实际业务中应该是将传入的code转义成对应的策略类的bean id。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
@PropertySource("classpath:application.properties")
@ConfigurationProperties(prefix = "strategy")
public class StrategyAliasConfig {
private HashMap<String, String> aliasMap;

public static final String DEFAULT_STATEGY_NAME = "defaultStrategy";

public HashMap<String, String> getAliasMap() {
return aliasMap;
}

public void setAliasMap(HashMap<String, String> aliasMap) {
this.aliasMap = aliasMap;
}

public String of(String entNum) {
return aliasMap.get(entNum);
}
}

配置文件application.properties

1
2
strategy.aliasMap.strategy1=concreteStrategy1
strategy.aliasMap.strategy2=concreteStrategy2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
public class StrategyFactory {
@Autowired
private StrategyAliasConfig strategyAliasConfig;

//Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
@Autowired
private Map<String, Strategy> strategyMap;

//找不到对应的策略类,使用默认的
public Strategy getBy(String strategyName) {
String name = strategyAliasConfig.of(strategyName);
if (name == null) {
return strategyMap.get(StrategyAliasConfig.DEFAULT_STATEGY_NAME);
}
Strategy strategy = strategyMap.get(name);
if (strategy == null) {
return strategyMap.get(StrategyAliasConfig.DEFAULT_STATEGY_NAME);
}
return strategy;

}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
class SpringbootDemoApplicationTests {

@Autowired
private ApplicationContext context;

@Test
public void test() {
context.getBean(StrategyFactory.class).getBy("strategy1").doSomething();
context.getBean(StrategyFactory.class).getBy("strategy2").doSomething();
}

}

执行结果如下:

1
2
具体策略1的运算法则...
具体策略2的运算法则...

参考

策略模式及使用Spring实现策略模式+工厂模式

评论