代码改变世界

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

2021-04-20 11:46  小草生活  阅读(776)  评论(0编辑  收藏  举报

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

我们实现某个接口时,可能会有很多种不同的实现方式。这些不同的实现方式通过一定的规则可以随意切换使用时,我们就可以考虑使用策略模式来实现。例如本文将要做的事情:打印TES与DWG进行BO5的所有结果。

一、定义
Define a family of algorithms,encapsulate each one, and make them interchangeable.

定义一组算法,将每个算法都封装起来,并且使他们之间可以转换。

二、UML类图
图片来自于《大话设计模式》

 

 

 

主要角色如下:

封装角色(Context):也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化
抽象策略角色(Strategy):策略家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性
具体策略角色(ConcreteStrategy):实现抽象策略中的操作,该类含有具体的算法
2、实现策略模式
//抽象策略角色
public interface Strategy {
//策略模式的运算法则
void doSomething();
}
1
2
3
4
5
//具体策略角色
public class ConcreteStrategy1 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略1的运算法则...");
}
}
1
2
3
4
5
6
7
//具体策略角色
public class ConcreteStrategy2 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略2的运算法则...");
}
}
1
2
3
4
5
6
7
//封装角色
public class Context {
//抽象策略
private Strategy strategy;

//构造函数设置具体策略
public Context(Strategy strategy) {
this.strategy = strategy;
}

//封装后的策略方法
public void doAnything() {
this.strategy.doSomething();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
//声明一个具体的策略
Strategy strategy = new ConcreteStrategy1();
//声明上下文对象
Context context = new Context(strategy);
//执行封装后的方法
context.doAnything();
}
}
1
2
3
4
5
6
7
8
9
10
执行结果如下:

具体策略1的运算法则...
1
二、使用Spring实现策略模式+工厂模式
1、实现策略类
public interface Strategy {
//策略模式的运算法则
void doSomething();
}
1
2
3
4
@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 "默认策略";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2、实现工厂类
@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);
}
}
1
2
3
4
5
6
7
8
9
10
Spring会自动将Strategy接口的实现类注入到这个Map中(前提是实现类得是交给Spring 容器管理的),这个Map的key为bean id,可以用@Component(value = "xxx")的方式设置,如果直接用默认的方式的话,就是首字母小写。value值则为对应的策略实现类

 

测试类:

@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
3
4
5
6
7
8
9
10
11
12
13
执行结果如下:

具体策略1的运算法则...
具体策略2的运算法则...
1
2
3、别名转换
上面测试类调用的使用使用的bean id,实际业务中应该是将传入的code转义成对应的策略类的bean id

@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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
配置文件application.properties

strategy.aliasMap.strategy1=concreteStrategy1
strategy.aliasMap.strategy2=concreteStrategy2
1
2
@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
14
15
16
17
18
19
20
21
22
23
测试类:

@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
3
4
5
6
7
8
9
10
11
12
13
执行结果如下:

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

 

特别注意,主要我遇到的坑有

1.spring 必须自己在unit test 中设置初始化

2. //该标签 EnableConfigurationProperties 才会让 configuration 注解生效,负责始终取不到配置文件内容,这个很重要

@EnableConfigurationProperties

测试截图

 

 

 

我整理的代码地址: https://github.com/Amos666/FactoryTest    

欢迎大家学习交流


参考:

《设计模式之禅》

https://mp.weixin.qq.com/s/cc4Slm0Ta22PShYuKpO2mQ
————————————————
版权声明:本文为CSDN博主「邋遢的流浪剑客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40378034/article/details/104121363