博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何使用策略模式处理多种类型请求
阅读量:7223 次
发布时间:2019-06-29

本文共 5298 字,大约阅读时间需要 17 分钟。

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 */@Servicepublic class ActivityStrategyFactory {    private static final Map
STRATEGY_BEAN_CACHE = Maps.newConcurrentMap(); @Autowired private ApplicationContext applicationContext; /** * 根据不同的场景创建不同的策略 * 实现思路:遍历策略列表的所有策略,获取策略的注解, * 比对场景值是否一致,场景值一致则返回当前策略的实例对象 * @param scene 场景值 * @return */ public ActivityStrategyInterface createStrategy(Integer scene) { Optional
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@Servicepublic class CreateActivityRecordMessageConsumer implements MessageConsumer
{ @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); }}复制代码

这是整个过程的全部代码,如果现在新增其它活动场景(比如布置假期作业),那么直接写一个布置假期作业的处理类,新增一个对应的场景值就可以了,对原有代码不侵入。

转载地址:http://dckfm.baihongyu.com/

你可能感兴趣的文章
前端_面试
查看>>
Promise面试题,控制异步流程
查看>>
MQ框架的比较
查看>>
Spark in action on Kubernetes - Playground搭建与架构浅析
查看>>
详解NodeJs流之一
查看>>
Fundebug计费标准解释:事件数是如何定义的?
查看>>
理解这几张图,你就是js小牛了
查看>>
【EOS】Cleos基础
查看>>
iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
查看>>
使用parted解决大于2T的磁盘分区
查看>>
oschina
查看>>
Octave 入门
查看>>
深度学习入门:10门免费线上课程推荐
查看>>
JavaScript 如何正确处理 Unicode 编码问题!
查看>>
iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
查看>>
微服务核心架构梳理
查看>>
浅谈JavaScript的面向对象和它的封装、继承、多态
查看>>
laravel with 查询列表限制条数
查看>>
Python爬虫--- 1.3 BS4库的解析器
查看>>
CentOS从零开始部署Nodejs项目
查看>>