大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说如何使用策略模式处理多种类型请求数据_全局页面替换策略,希望您对编程的造诣更进一步.
1.需求简述
现在有一个活动,活动场景包含布置书籍作业,布置短文作业,布置一课一练作业(以后还可能会新增其它类型的活动),每一种活动场景有自己对应的完成逻辑和奖励。现在定义对应的场景值如下:
活动名称 | 活动场景值 |
---|---|
布置书籍作业 | 11 |
布置短文作业 | 12 |
布置一课一练作业 | 13 |
2.解决方案
解决方案一:因为布置作业和完成活动属于不同的项目,我采用的是消息队列的方式(消息队列不是本文讨论的重点),布置作业时发送消息,传递对应的活动场景值和其它必须的参数过来,消费端收到消息之后,根据对应的场景值作出相应的处理,伪代码如下:
if(场景值==11){
//完成书籍作业的相关逻辑
}else if(场景值==12){
//完成短文作业的相关逻辑
}else if(场景值==13){
//完成一课一练作业的相关逻辑
}
这种方式是最简单的,也是最容易理解的,但是存在的问题是,如果现在新增新的活动场景,原来的if else后面要新增新的代码和判断逻辑,对原有的代码具有侵入性;再者如果类型非常多的话,if else也会有很多,代码看起来不够优雅。 解决方案二:采用策略模式来解决,定义一个策略接口,布置书籍作业,短文作业,一课一练的处理逻辑都实现策略接口,根据传入的不同场景值选择不同的处理类。这也是使用策略模式最难的地方:如何根据传入的参数,找到对应的处理类,答案是:可以采用spring的getBean,或者是java的反射。我在程序启动时就加载所有的策略类到内存中,处理请求时,根据传入的参数,选择对应的处理类。
3.实操代码
3.1定义策略接口
/** * 活动策略接口 * @author junzhongliu * @date 2018/9/30 17:11 */
public interface ActivityStrategyInterface {
/** * 教师创建或更新活动记录 * @param userId 用户id * @param scene 场景 * @param condition 本活动完成的条件 */
void doActivityAction(Long userId,Integer scene,Integer condition);
}
3.2自定义注解
为了方便比对传入的场景值,选择对应的策略处理类,我自定义了一个注解
/** * 活动场景注解 * @author junzhongliu * @date 2018/9/30 17:24 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivitySceneAnnotation {
/** * 活动场景id,默认值1 */
int sceneId() default 1;
}
3.3定义对应的策略处理接口
就是真正处理布置书籍作业,短文作业,一课一练作业的策略实现类,它们是要实现策略接口的,代码如下:
/** * 布置书籍任务 * @author junzhongliu * @date 2018/9/30 17:13 */
@Slf4j
@Service
@ActivitySceneAnnotation(sceneId = ActivitySceneConstants.BOOK_ACTIVITY)
public class BookStrategy implements ActivityStrategyInterface {
@Autowired
TeacherActivityRecordService teacherActivityRecordService;
@Override
public void doActivityAction(Long userId, Integer scene, Integer condition) {
log.info("desc:{},userId:{},scene:{},condition:{}","布置书籍任务[STRATEGY]",userId,scene,condition);
teacherActivityRecordService.saveOrUpdateRookieActivityRecord(userId,scene,condition);
}
}
这里的ActivitySceneAnnotation就是上面我们自定义的注解,ActivitySceneConstants.BOOK_ACTIVITY是自定义的常量,真实值是11,对应上面的场景值,可以看到在处理的过程中是调用了service来处理的,其它的短文任务,一课一练任务跟这个基本一样的,只是不同的场景值,只在展示一个布置短文作业的:
/** * 完成短文活动任务 * @author junzhongliu * @date 2018/9/30 17:13 */
@Slf4j
@Service
@ActivitySceneAnnotation(sceneId = ActivitySceneConstants.PASSAGE_ACTIVITY)
public class PassageStrategy implements ActivityStrategyInterface {
@Resource
TeacherActivityRecordService teacherActivityRecordService;
@Override
public void doActivityAction(Long userId, Integer scene, Integer condition) {
log.info("desc:{},userId:{},scene:{},condition:{}","布置短文任务[STRATEGY]",userId,scene,condition);
teacherActivityRecordService.saveOrUpdateRookieActivityRecord(userId,scene,condition);
}
}
3.4根据场景值选择对应的处理类
我这里是在程序启动时,采用@PostConstruct注解,将实现ActivityStrategyInterface接口的所有策略类都加载到内存中了,用户请求传过来一个场景值,根据这个场景值,选择对应的处理类,全部代码如下:
/** * 策略处理类的工厂类 * @author junzhongliu * @date 2018/9/30 17:17 */
@Service
public class ActivityStrategyFactory {
private static final Map<String,ActivityStrategyInterface> STRATEGY_BEAN_CACHE = Maps.newConcurrentMap();
@Autowired
private ApplicationContext applicationContext;
/** * 根据不同的场景创建不同的策略 * 实现思路:遍历策略列表的所有策略,获取策略的注解, * 比对场景值是否一致,场景值一致则返回当前策略的实例对象 * @param scene 场景值 * @return */
public ActivityStrategyInterface createStrategy(Integer scene) {
Optional<ActivityStrategyInterface> strategyOptional =
STRATEGY_BEAN_CACHE
.entrySet()
.stream()
.map(e -> {
ActivitySceneAnnotation validScene = e.getValue().getClass().getDeclaredAnnotation(ActivitySceneAnnotation.class);
if (Objects.equals(validScene.sceneId(),scene)) {
return e.getValue();
}
return null;
}).filter(Objects::nonNull)
.findFirst();
if(strategyOptional.isPresent()){
return strategyOptional.get();
}
throw new RuntimeException("策略获得失败");
}
/** * 初始化策略列表 */
@PostConstruct
private void init() {
STRATEGY_BEAN_CACHE.putAll(applicationContext.getBeansOfType(ActivityStrategyInterface.class));
}
}
到现在为止,策略相关的处理已经定义完了,接下来看如何使用
3.5如何使用
我是在消息的消费处调用了ActivityStrategyFactory,传入场景值,获取处理类,代码如下:
/** * 消费其它模块的消息,创建或更新教师活动记录 * @author junzhongliu * @date 2018/9/30 16:50 */
@Slf4j
@Service
public class CreateActivityRecordMessageConsumer implements MessageConsumer<CreateActivityRecordMessage> {
@Autowired
private ActivityStrategyFactory strategyFactory;
@Override
public CreateActivityRecordMessage newMessageInstance() {
return new CreateActivityRecordMessage();
}
@Override
public void consume(CreateActivityRecordMessage message) throws Exception {
log.info("desc:{},param:{}","创建任务记录消费消息[CONSUMER]",JSONObject.toJSONString(message));
Long userId = message.getUserId();
Integer scene = message.getScene();
Integer condition = message.getCondition();
if(Objects.isNull(userId) || Objects.isNull(scene) || Objects.isNull(condition)){
return;
}
//创建具体的执行策略,并执行活动行为
ActivityStrategyInterface strategy = strategyFactory.createStrategy(message.getScene());
strategy.doActivityAction(userId,scene,condition);
}
}
这是整个过程的全部代码,如果现在新增其它活动场景(比如布置假期作业),那么直接写一个布置假期作业的处理类,新增一个对应的场景值就可以了,对原有代码不侵入。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13671.html