策略模式

pkL2R7q.png

作为程序员,可以选择多种编译器开发软件。

想象一下,你是一个旅行者,计划去不同的城市旅行。每个城市都有不同的交通方式:有些地方适合开车,有些地方适合骑自行车,还有些地方适合步行。

策略模式就像是你旅行时的交通方式选择方案。你可以根据目的地和个人喜好,灵活地选择最合适的交通方式。

案例-评分模块选择多种策略

需求:针对不同的应用类别和评分策略,编写不同的实现逻辑。

核心实现方式:策略模式

策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装到独立的类中,使得它们可以相互替换。

在本项目的场景中,输入的参数是一致的(应用和用户的答案列表),并且每种实现逻辑区别较大,很适合使用策略模式。

1.定义策略接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 评分策略
*/
public interface ScoringStrategy {

/**
* 执行评分
* @param choices
* @param app
* @return
* @throws Exception
*/
UserAnswer doScore(List<String> choices, App app)throws Exception;
}

2. 两种策略以及实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CustomScoreScoringStrategy implements ScoringStrategy{
@Override
public UserAnswer doScore(List<String> choices, App app) throws Exception{
//第一种算法策略
}
}


public class CustomTestScoringStrategy implements ScoringStrategy {
@Override
public UserAnswer doScore(List<String> choices, App app) throws Exception{
//第二种算法策略
}
}

3. 定义注解

声明式,在每个策略类中通过接口声明对应的生效条件,适合比较规律的策略选取场景。

注解:

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ScoringStrategyConfig {

//应用类型
int appType();

//评分策略
int scoringStrategy();
}

然后为其它两个实现类补充注解,根据不同策略,注解给不同的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ScoringStrategyConfig(appType = 0, scoringStrategy = 0)
public class CustomScoreScoringStrategy implements ScoringStrategy{
@Override
public UserAnswer doScore(List<String> choices, App app) throws Exception{
//第一种算法策略
}
}

@ScoringStrategyConfig(appType = 1, scoringStrategy = 0)
public class CustomTestScoringStrategy implements ScoringStrategy {
@Override
public UserAnswer doScore(List<String> choices, App app) throws Exception{
//第二种算法策略
}
}

4. 全局执行器

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
/**
* 评分策略执行器
*/
@Service
public class ScoringStrategyExecutor {

//策略列表
@Resource
private List<ScoringStrategy> scoringStrategyList;


public UserAnswer doScore(List<String> choiceList, App app) throws Exception {
Integer appType = app.getAppType();
Integer scoringStrategy = app.getScoringStrategy();
if (appType == null || scoringStrategy == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用类型或评分策略不能为空");
}

//根据注解获取策略
// 遍历scoringStrategyList中的每一个ScoringStrategy
for (ScoringStrategy strategy : scoringStrategyList) {
// 判断strategy的类是否被ScoringSrategyConfig注解修饰
if (strategy.getClass().isAnnotationPresent(ScoringSrategyConfig.class)) {
// 获取strategy的类上的ScoringSrategyConfig注解
ScoringSrategyConfig scoringSrategyConfig = strategy.getClass().getAnnotation(ScoringSrategyConfig.class);
// 判断ScoringSrategyConfig注解中的appType和scoringStrategy是否与传入的appType和scoringStrategy相等
if(scoringSrategyConfig.appType()==appType && scoringSrategyConfig.scoringStrategy()==scoringStrategy){
return strategy.doScore(choiceList,app);
}
}
}
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用配置有误,未找到匹配的策略");
}

}

因为用了 ScoringStrategyConfig 注解,所以这个实现类被加上了 component 注解,因此可以被spring 管理扫描到。 然后 @Resoure 注入的时候,会通过 ScoringStrategy 类型找到所有实现 ScoringStrategy 接口的实现类

这样使用注解,就不需要增加策略的时候修改全局执行器的代码,他可以根据注解里面的值自动选择相对于的策略实现