基于注解实现策略模式

基于注解实现策略模式

viEcho Lv5

背景: 在佣金项目时,查询佣金总额接口根据dbType的不同走的不同的if else逻辑;然后让改为策略模式的实现,这里将整个改造的过程记录在此;

1
2
3
4
5
6
7
8
9
10
11
12
// 原始代码如下
if(dbType == 1){
//查主库
}else if(dbType == 2){
//查从库
}else if(dbType == 3){
//查缓存
}else if(dbType == 4){
//优先查缓存,缓存不存在再查主库
}else if(dbType == 5){
//优先查缓存,缓存不存在再查从库
}

新增strategyType注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 策略类型
*
* @author echo
* @date 2024/01/12
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StrategyType {

Class<? extends BaseCommonStrategy<?,?,?>> value();

}

新增基础策略接口

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
/**
* 公共策略接口
*
* @author echo
* @date 2024/01/12
* P 代表params参数
* K 代表路由key
* R 代表返回结果
*/
public interface BaseCommonStrategy <P,K,R>{

/**
* 路由关键字
*
* @return {@link K}
*/
K routeKey();

/**
* 处理
*
* @param p p
* @return {@link R}
*/
R handle(P p);

}

新增策略工厂类

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
/**
* 公共策略工厂
*
* @author echo
* @date 2024/01/12
*/
@Service
@Slf4j
public class CommonStrategyFactory {

/**
* 两层级map
*/
private static final TwoLevelConcurrentHashMap<Class<? extends BaseCommonStrategy<?,?,?>>, Object,BaseCommonStrategy<?,?,?>> STRATEGY = new TwoLevelConcurrentHashMap<>();


/**
* 建立战略工厂
*
* @param routes 路线
*/
@Autowired
private void buildStrategyFactory(List<BaseCommonStrategy<?,?,?>> routes){
log.info("构建通用策略start:{}", routes.size());
routes.forEach(strategy->{
Class<? extends BaseCommonStrategy> aClass = strategy.getClass();
StrategyType annotation = AnnotationUtils.findAnnotation(aClass, StrategyType.class);
if(null==annotation){
log.error("构建策略路由失败,找不到对应的annotation.clazz:{}", aClass);
throw new GlobalException(ResponseCodeEnum.ANNOTATION_CAN_NOT_FIND);
}
STRATEGY.put(annotation.value(),strategy.routeKey(),strategy);
});
}


/**
* 处理
*
* @param aClass 策略类
* @param routeKey 路由关键字
* @param parameters 参数
* @return {@link R}
*/
public <P,K,R,S extends BaseCommonStrategy<P,K,R>> R handle(Class<? extends BaseCommonStrategy<P,K,R>> aClass,K routeKey,P parameters){
String simpleName = aClass.getSimpleName();
try{
BaseCommonStrategy<P, K, R> strategy = (BaseCommonStrategy<P, K, R>) STRATEGY.get(aClass, routeKey);
if(null == strategy){
log.error("未找到对应的处理策略,routeKey:{},parameters:{}", routeKey, JSON.toJSON(parameters));
throw new GlobalException(ResponseCodeEnum.STARTEGY_CAN_NOT_FIND,simpleName+"未找到对应的处理策略");
}
return strategy.handle(parameters);
}catch (Exception e){
log.error("strategy handle occurs error:"+e);
throw new GlobalException(e.getMessage());
}
}

/**
* 两级map
*
* @author echo
* @date 2024/01/12
*/
public static class TwoLevelConcurrentHashMap<K, R, P extends BaseCommonStrategy<?, ?, ?>> {
/**
* 层级map
*/
private ConcurrentHashMap<K, ConcurrentHashMap<R, P>> levelMap;

/**
* 两级map
*/
public TwoLevelConcurrentHashMap() {
this.levelMap = new ConcurrentHashMap<>();
}

/**
* 放
*
* @param k k
* @param r r
* @param p p
*/
public void put(K k,R r,P p){
if(levelMap.containsKey(k)){
ConcurrentHashMap<R, P> rpConcurrentHashMap = levelMap.get(k);
rpConcurrentHashMap.put(r,p);
levelMap.put(k, rpConcurrentHashMap);
return;
}
ConcurrentHashMap<R, P> hashMap = new ConcurrentHashMap<>();
hashMap.put(r,p);
levelMap.put(k,hashMap);
}

/**
* 取
*
* @param aClass 策略类
* @param routeKey 路由关键字
* @return {@link P}
*/
public P get(K aClass, R routeKey) {
ConcurrentHashMap<R, P> rpConcurrentHashMap = levelMap.get(aClass);
return rpConcurrentHashMap.get(routeKey);
}
}
}

以上是核心实现,将同类型的策略类放到map中,第一层key为注解里的顶层策略类class,第二层key为路由key,value则为对应的策略实现类;

设计顶层查询接口

1
2
3
4
5
6
7
8
9
/**
* 基本用户查询策略
*
* @author echo
* @date 2024/01/12
*/
public interface BaseUserQueryStrategy<P,K,R> extends BaseCommonStrategy<P,K,R> {

}

设计基于注解区分的抽象类

1
2
3
4
5
6
7
8
9
10
/**
* 基于数据库类型策略基本用户查询
*
* @author echo
* @date 2024/01/12
*/
@StrategyType(BaseUserQueryByDbTypeStrategy.class)
public abstract class BaseUserQueryByDbTypeStrategy implements BaseUserQueryStrategy<Long,Integer, UserInfo>{

}

各策略类的实现

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
* 按主库略查询用户
*
* @author echo
* @date 2024/01/12
*/
@Service
@Slf4j
public class QueryUserByMasterStrategy extends BaseUserQueryByDbTypeStrategy{

/**
* 主库用户服务
*/
@Autowired
private MasterUserService masterUserService;

/**
* 路由关键字
*
* @return {@link Integer}
*/
@Override
public Integer routeKey() {
return DbTypeEnums.MASTER.getCode();
}

/**
* 处理
*
* @param id 主键
* @return {@link UserInfo}
*/
@Override
public UserInfo handle(Long id) {
log.info("查询主库。。。");
return masterUserService.queryUserInfoById(id);
}
}

/**
* 按从库策略查询用户
*
* @author echo
* @date 2024/01/12
*/
@Service
@Slf4j
public class QueryUserBySlaveStrategy extends BaseUserQueryByDbTypeStrategy{

/**
* 从库用户服务
*/
@Autowired
private SlaveUserService slaveUserService;

/**
* 路由关键字
*
* @return {@link Integer}
*/
@Override
public Integer routeKey() {
return DbTypeEnums.SLAVE.getCode();
}

@Override
public UserInfo handle(Long id) {
log.info("查询从库。。。");
return slaveUserService.queryUserInfoById(id);
}
}

/**
* 按缓存策略查询用户
*
* @author echo
* @date 2024/01/12
*/
@Service
@Slf4j
public class QueryUserByCacheStrategy extends BaseUserQueryByDbTypeStrategy{


/**
* 路由关键字
*
* @return {@link Integer}
*/
@Override
public Integer routeKey() {
return DbTypeEnums.CACHE.getCode();
}

/**
* 处理
*
* @param id 主键
* @return {@link UserInfo}
*/
@Override
public UserInfo handle(Long id) {
log.info("查询缓存。。。");
// 查询缓存
return new UserInfo();
}
}

/**
* 按缓存和主库策略查询用户
*
* @author echo
* @date 2024/01/12
*/
@Service
@Slf4j
public class QueryUserByCacheAndMasterStrategy extends BaseUserQueryByDbTypeStrategy{

/**
* 主库用户服务
*/
@Autowired
private MasterUserService masterUserService;

/**
* 路由关键字
*
* @return {@link Integer}
*/
@Override
public Integer routeKey() {
return DbTypeEnums.CACHE_AND_MASTER.getCode();
}

/**
* 处理
*
* @param id 主键
* @return {@link UserInfo}
*/
@Override
public UserInfo handle(Long id) {
log.info("查询缓存和主库。。。");
// 先查询缓存
// 如果缓存查询为空,再查询主库。。。代码略
return masterUserService.queryUserInfoById(id);
}
}

/**
* 按缓存和从库策略查询用户
*
* @author echo
* @date 2024/01/12
*/
@Service
@Slf4j
public class QueryUserByCacheAndSlaveStrategy extends BaseUserQueryByDbTypeStrategy{

/**
* 从库用户服务
*/
@Autowired
private SlaveUserService slaveUserService;

/**
* 路由关键字
*
* @return {@link Integer}
*/
@Override
public Integer routeKey() {
return DbTypeEnums.CACHE_AND_SLAVE.getCode();
}

/**
* 处理
*
* @param id 主键
* @return {@link UserInfo}
*/
@Override
public UserInfo handle(Long id) {
log.info("查询缓存和从库。。。");
// 先查询缓存
// 如果缓存查询为空,再查询从库。。。代码略
return slaveUserService.queryUserInfoById(id);
}
}

测试接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 策略工厂
*/
@Autowired
private CommonStrategyFactory commonStrategyFactory;

/**
* 查询用户信息
*
* @param id 主键
* @param dbType db类型
* @return {@link ResponseVO}
*/
@GetMapping("/query")
ResponseVO queryUserInfo(@PathParam("id") Long id, @PathParam("dbType") Integer dbType) {
ResponseVO vo = new ResponseVO();
UserInfo userInfo = commonStrategyFactory.handle(BaseUserQueryByDbTypeStrategy.class, dbType, id);
return vo.data(userInfo);
}

测试

然后根据dbType的不同会切换到不同的策略类执行对应的handle方法实现;

总结:策略模式的作用是: 将策略类与调用者分离,调用者只需要知道调用接口,而不需要知道具体的实现类,从而达到解耦的目的。以上就是基于注解实现的一个策略模式,主要是核心点在于两层级map的实现及基于注解的策略类;

  • Title: 基于注解实现策略模式
  • Author: viEcho
  • Created at : 2024-01-10 18:23:33
  • Updated at : 2025-02-21 18:18:40
  • Link: https://viecho.github.io/2024/0110/strategy-pattern.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments