内容简介:现在有一个活动,活动场景包含布置书籍作业,布置短文作业,布置一课一练作业(以后还可能会新增其它类型的活动),每一种活动场景有自己对应的完成逻辑和奖励。现在定义对应的场景值如下:解决方案一:因为布置作业和完成活动属于不同的项目,我采用的是消息队列的方式(消息队列不是本文讨论的重点),布置作业时发送消息,传递对应的活动场景值和其它必须的参数过来,消费端收到消息之后,根据对应的场景值作出相应的处理,伪代码如下:这种方式是最简单的,也是最容易理解的,但是存在的问题是,如果现在新增新的活动场景,原来的if else
现在有一个活动,活动场景包含布置书籍作业,布置短文作业,布置一课一练作业(以后还可能会新增其它类型的活动),每一种活动场景有自己对应的完成逻辑和奖励。现在定义对应的场景值如下:
活动名称 | 活动场景值 |
---|---|
布置书籍作业 | 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); } } 复制代码
这是整个过程的全部代码,如果现在新增其它活动场景(比如布置假期作业),那么直接写一个布置假期作业的处理类,新增一个对应的场景值就可以了,对原有代码不侵入。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
轻量级Django
茱莉亚·埃尔曼 (Julia Elman)、马克·拉温 (Mark Lavin) / 侯荣涛、吴磊 / 中国电力出版社; 第1版 / 2016-11-1 / 35.6
自Django 创建以来,各种各样的开源社区已经构建了很多Web 框架,比如JavaScript 社区创建的Angular.js 、Ember.js 和Backbone.js 之类面向前端的Web 框架,它们是现代Web 开发中的先驱。Django 从哪里入手来适应这些框架呢?我们如何将客户端MVC 框架整合成为当前的Django 基础架构? 本书讲述如何利用Django 强大的“自支持”功......一起来看看 《轻量级Django》 这本书的介绍吧!