在业务代码中,经常会写策略调度器,还有自动注入策略的方法。极大的降低了开发的效率。所以用Java的反射功能来实现一个策略模式的工具类。
代码
定义一个策略接口。
1
2
3
4
5
6
7
8
9
10
11package cn.gloduck.common.strategy;
/**
* 策略接口,无实际作用。
* 所有的自定义策略接口必须继承此类
*
* @author Gloduck
* @date 2021/11/09
*/
public interface Strategy {
}在定义一个注解,用于表明当前类是一个策略。
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
26package cn.gloduck.common.strategy;
import java.lang.annotation.*;
/**
* 策略组件
*
* @author Gloduck
* @date 2021/11/09
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StrategyComponent {
/**
* 策略接口的class
* @return class
*/
Class<? extends Strategy> strategyInterface();
/**
* 策略名称,单个策略接口名称不能有重复的
* @return 策略名称
*/
String name();
}策略的调度器,用户获取具体的策略。这里使用泛型来定义。
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
52package cn.gloduck.common.strategy;
import java.util.HashMap;
import java.util.Map;
/**
* 存放单个策略的调度器
*
* @author Gloduck
* @date 2021/11/09
*/
public class StrategyContext<T> {
private final Map<String, T> strategyMap;
StrategyContext() {
this.strategyMap = new HashMap<>(16);
}
StrategyContext(int size) {
int cap = size / 3 * 4 + 1;
this.strategyMap = new HashMap<>(cap);
}
/**
* 注册策略
*
* @return boolean
*/
void registerStrategy(String name, T strategy) {
this.strategyMap.put(name, strategy);
}
/**
* 获取策略
*
* @param name 策略名称
* @return {@link T}
*/
public T getStrategy(String name) {
return strategyMap.get(name);
}
/**
* 策略数量
*
* @return int
*/
public int size() {
return strategyMap.size();
}
}最后是一个容器,存储了所有的策略。
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154package cn.gloduck.common.strategy;
import cn.gloduck.common.exception.strategy.InvalidStrategyException;
import cn.gloduck.common.exception.strategy.NoStrategyFoundException;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 策略工厂,存放所有的策略
* 实现一个策略首先先定义一个策略接口[需要继承{@link Strategy}接口],然后写策略实现类。策略实现类必须使用{@link StrategyComponent}注解,并且{@link StrategyComponent#strategyInterface()}需要和定义的策略接口对应
*
* @author Gloduck
* @date 2021/11/09
*/
public class StrategyFactory {
private final Map<Class<? extends Strategy>, StrategyContext> strategyContextMap;
private static final StrategyFactory INSTANCE = new StrategyFactory();
private StrategyFactory() {
this.strategyContextMap = new HashMap<>();
}
/**
* 注册策略
*
* @param strategyInterface 策略接口
* @param strategyName 策略名称
* @param strategy 策略
*/
public <K extends Strategy, V extends K> void registerStrategy(Class<K> strategyInterface, String strategyName, V strategy) {
if (strategyInterface == null || strategy == null) {
throw new InvalidStrategyException("策略相关的类不能为空");
}
if (!strategyInterface.isInterface()) {
throw new InvalidStrategyException("类【" + strategyInterface.getName() + "】不是接口");
}
doRegister(strategyInterface, strategyName, strategy);
}
/**
* 注册策略
*
* @param strategyInterface 策略接口
* @param strategyName 策略名称
* @param strategy 策略
*/
private <T extends Strategy> void registerScannedStrategy(Class<T> strategyInterface, String strategyName, Object strategy) {
Class<?>[] interfaces = strategy.getClass().getInterfaces();
boolean hasInterface = false;
for (Class<?> anInterface : interfaces) {
// 检查是否实现了StrategyComponent中定义的接口
if (strategyInterface.equals(anInterface)) {
hasInterface = true;
break;
}
}
if (!hasInterface) {
throw new InvalidStrategyException("注册策略失败,请检查类【" + strategy.getClass().getName() + "】是否实现了StrategyComponent注解中定义的【" + strategyInterface.getName() + "】接口");
}
doRegister(strategyInterface, strategyName, strategy);
}
/**
* 执行注册
* 调用此方法请优先对strategy进行合法性检验,确保是strategyInterface的实现类
*
* @param strategyInterface 策略接口
* @param strategyName 策略名称
* @param strategy 策略
*/
@SuppressWarnings("unchecked")
private <T extends Strategy> void doRegister(Class<T> strategyInterface, String strategyName, Object strategy) {
if (!strategyContextMap.containsKey(strategyInterface)) {
// 不存在则添加
StrategyContext<T> context = new StrategyContext<>();
strategyContextMap.put(strategyInterface, context);
}
StrategyContext<T> context = strategyContextMap.get(strategyInterface);
context.registerStrategy(strategyName, (T) strategy);
}
/**
* 获取策略
*
* @param strategyInterface 策略接口
* @return {@link T}
*/
@SuppressWarnings("unchecked")
public <T extends Strategy> StrategyContext<T> getStrategyContext(Class<T> strategyInterface) {
StrategyContext<T> strategyContext = strategyContextMap.get(strategyInterface);
if (strategyContext == null || strategyContext.size() == 0) {
throw new NoStrategyFoundException("无法找到对应的策略【" + strategyInterface.getName() + "】");
}
return strategyContext;
}
/**
* 扫描包并注册策略
*
* @param basePackage 基本包
*/
public void scanPackageAndRegisterStrategy(String basePackage) {
// 获取所有带StrategyComponent注解的类
Set<Class<?>> scanPackage = ClassUtil.scanPackage(basePackage, tClass -> {
boolean accept = false;
try {
accept = tClass.getAnnotation(StrategyComponent.class) != null;
} catch (Throwable e) {
}
return accept;
});
scanPackage.forEach(tClass -> {
// 获取注解
StrategyComponent strategyComponent = tClass.getAnnotation(StrategyComponent.class);
Class<? extends Strategy> strategyInterface = strategyComponent.strategyInterface();
registerScannedStrategy(strategyInterface, strategyComponent.name(), createInstance(tClass));
});
}
/**
* 扫描包并注册策略
* 直接空扫描会导致性能损耗,建议使用{@link StrategyFactory#scanPackageAndRegisterStrategy(String)}
*/
@Deprecated
public void scanPackageAndRegisterStrategy() {
scanPackageAndRegisterStrategy("");
}
/**
* 反色调用无参构造函数创建对象
*
* @param tClass 类
* @return {@link T}
*/
private <T> T createInstance(Class<T> tClass) {
return ReflectUtil.newInstance(tClass);
}
/**
* 获取实例
*
* @return {@link StrategyFactory}
*/
public static StrategyFactory instance() {
return INSTANCE;
}
}
使用
使用流程
- 定义一个策略的接口,然后这个接口继承
Strategy
类。 - 然后写一些策略实现这个策略接口,并且使用
StrategyComponent
注解。 - 最后将策略注册到容器中。通过调度器来使用。
测试代码
- 这里定义了两个完全不同的策略,可以理解为在业务中这两个策略是完全没有关系的。如果不使用工具的话要为这些策略没一个都写一个调度器。使用了工具的话则不需要,添加其他的策略模式扩展会方便很多。
1 |
|
运行结果
1
2这是业务策略1
这是控制器策略2