一、背景:为什么要用到策略模式?
阿里开发规约-编程规约-控制语句-第六条 :超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现。
普通代码示例:
if (a == 1) {
...
} else if ( a == 2 ) {
...
} else if (a == 3) {
...
} else {
...
}
这种代码虽然写起来简单,但是很明显违反了面向对象的 2 个基本原则:
- 单一职责原则(一个类应该只有一个发生变化的原因):因为之后修改任何一个逻辑,当前类都会被修改
- 开闭原则(对扩展开放,对修改关闭):如果此时需要添加(删除)某个逻辑,那么不可避免的要修改原来的代码
因为违反了以上两个原则,尤其是当 if-else 块中的代码量比较大时,后续代码的扩展和维护就会逐渐变得非常困难且容易出错,使用卫语句也同样避免不了以上两个问题。因此根据我的经验,得出一个我个人认为比较好的实践:
- if-else 不超过 2 层,块中代码 1~5 行,直接写到块中,否则封装为方法
- if-else 超过 2 层,但块中的代码不超过 3 行,尽量使用卫语句
if (a == 1) {
...
}
if (a == 2) {
...
}
if (a == 3){
...
}
- if-else 超过 2 层,且块中代码超过 3 行,尽量使用策略模式
二、策略模式如何实现
- 定义策略接口
/**
* @author zheyue
* @date 2021-09-26 11:07:29
*/
public interface StrategyPatternService {
/**
* 根据具体的ActionName执行具体的Action
* @return
*/
String getActionName();
/**
* 执行的具体的action
*/
void doAction();
}
- 相关策略实现
/**
* @author zheyue
* @date 2021/9/26
**/
@Slf4j
@Service
public class AlertActionServiceImpl implements StrategyPatternService{
@Override
public String getActionName() {
return "alert";
}
@Override
public void doAction() {
// 执行具体的Action
log.warn("alert!");
}
}
/**
* @author zheyue
* @date 2021/9/26
**/
@Slf4j
@Service
public class DeleteActionServiceImpl implements StrategyPatternService{
@Override
public String getActionName() {
return "delete";
}
@Override
public void doAction() {
log.warn("delete!");
}
}
/**
* @author zheyue
* @date 2021/9/26
**/
@Slf4j
@Service
public class InsertActionServiceImpl implements StrategyPatternService{
@Override
public String getActionName() {
return "insert";
}
@Override
public void doAction() {
log.warn("insert");
}
}
- 借助Spring来完成注入
/**
* @author zheyue
* @date 2021/9/26
**/
@Component
public class StrategyPatternAdapter implements ApplicationContextAware {
/**
* 根据ActionName将具体的Action存放到Map中
*/
private Map<String,StrategyPatternService> strategyPatternMap = new HashMap<>();
public StrategyPatternService getStrategyService(String actionName){
return strategyPatternMap.get(actionName);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, StrategyPatternService> strategyServiceMap = applicationContext.getBeansOfType(StrategyPatternService.class);
strategyServiceMap.values().forEach(strategyPatternService -> {
strategyPatternMap.put(strategyPatternService.getActionName(), strategyPatternService);
});
}
}
- 应用(这个为了方便测试,我写到了controller,大家在应用的过程中,根据自己的情况放到具体的业务处理层)
@Resource
private StrategyPatternAdapter strategyPatternAdapter;
@RequestMapping("/")
public void testStrategy() {
List<String> actionList = Lists.newArrayList("insert", "alert");
actionList.stream().forEach(actionName -> {
StrategyPatternService strategyService = strategyPatternAdapter.getStrategyService(actionName);
if (strategyService != null) {
strategyService.doAction();
}
});
}
-
执行结果
-
如何有新的策略,只需要继承策略接口,完成具体的策略实现,此时不需要修改任何代码,因为Spring会通过ApplicationContext帮助我们自动完成注入实现。
三、拓展
如果我想顺序的执行各个Action,该如何做到呢?
- 策略接口新增排序的标识
/**
* @author zheyue
* @date 2021-09-26 11:07:29
*/
public interface StrategyPatternService {
/**
* 根据具体的ActionName执行具体的Action
* @return
*/
String getActionName();
/**
* 执行的具体的action
*/
void doAction();
/**
* 条件优先级,数字越低,优先级越高
* @return
*/
int priority();
}
● 具体的Action实现这个方法,自定义优先级
/**
* @author zheyue
* @date 2021/9/26
**/
@Slf4j
@Service
public class AlertActionServiceImpl implements StrategyPatternService{
@Override
public String getActionName() {
return "alert";
}
@Override
public void doAction() {
// 执行具体的Action
log.warn("alert!");
}
@Override
public int priority() {
return 1;
}
}
/**
* @author zheyue
* @date 2021/9/26
**/
@Slf4j
@Service
public class InsertActionServiceImpl implements StrategyPatternService{
@Override
public String getActionName() {
return "insert";
}
@Override
public void doAction() {
log.warn("insert");
}
@Override
public int priority() {
return 5;
}
}
/**
* @author zheyue
* @date 2021/9/26
**/
@Slf4j
@Service
public class DeleteActionServiceImpl implements StrategyPatternService{
@Override
public String getActionName() {
return "delete";
}
@Override
public void doAction() {
log.warn("delete!");
}
@Override
public int priority() {
return 10;
}
}
● Adapter作如下修改
public List<StrategyPatternService> strategyPatternServiceList;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, StrategyPatternService> strategyServiceMap = applicationContext.getBeansOfType(StrategyPatternService.class);
strategyPatternServiceList = strategyServiceMap.values()
.stream()
.sorted(Comparator.comparing(StrategyPatternService::priority))
.collect(Collectors.toList());
}
/**
* 执行具体的Action
*/
public void doAction(){
for (StrategyPatternService strategyPatternService : strategyPatternServiceList) {
strategyPatternService.doAction();
}
}
- 执行结果