如何使用策略模式处理多种类型请求

2023-11-04

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);
    }

}
复制代码

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用策略模式处理多种类型请求 的相关文章

  • 抽象超类的默认接口方法

    可以说我有以下结构 abstract class A abstract boolean foo interface B default boolean foo return doBlah class C extends A implemen
  • 不同的 JDK 更新会产生不同的 Java 字节码吗?

    假设场景 我有一个项目 其源合规性级别指定为 1 5 现在 我使用两种不同的 JDK 编译此项目 首先使用 JDK 6 Update 7 然后使用 JDK 6 Update 20 这两个不同的 JDK 是否会生成不同的 Java 字节代码
  • 在 MongoDB Java 驱动程序中如何使用 $filter

    我有一个适用于 MQL 的查询 我需要将其翻译成Java MQL 中的查询如下所示 db
  • 使用 Spring MVC 返回 PDF 文件

    实际上 我有这个功能 我有一个框架 可以在其中设置 URL ip port birt preview report report rptdesign format pdf parameters 并且该框架呈现 PDF 文件 但我想隐藏该网址
  • Spring Data:限制自定义查询的结果

    在我的 Spring 数据存储库中 我 必须 使用自定义查询 Query注解 我知道我可以限制这样的命名查询中的结果数量 Iterable
  • Eclipse 自动完成更改变量名称

    只是一个愚蠢的问题 但很难搜索 因为有很多关于 Eclipse 自动完成的主题 而且很难找到与我的问题匹配的内容 所以问题是 如果我写 MyClass MyVarName 然后按空格键 添加 new MyClass Eclipse 自动添加
  • 获取TextView的高度

    我有一些文字将被放入TextView 我这样做是使用setText 现在我需要找到文本在文本中占据的行数或高度TextView 我尝试使用getHeight 但它总是返回 0 无论如何 有没有办法获得文本中存在的文本的高度TextView
  • 在java.util中获取错误ArrayList不带参数[重复]

    这个问题在这里已经有答案了 我已经创建了一个类 Student 现在我尝试将我的 Student 对象存储在 ArrayList 中 但在编译 ArrayList 不接受参数时出现错误 我已经检查了我的代码很多次 但找不到问题所在 我的学生
  • 将 EditText 聚焦在设备上运行的 PopupWindow 中时出现异常

    我正在为 Android 开发一个弹出窗口 它正在工作 我在上面添加了一个 EditText 和一个按钮 当在 ADV 上运行时 它可以正常工作 而在设备上运行时 当我专注于 EditText 时 这会抛出一个奇怪的异常 android v
  • java.lang.UnsupportedOperationException:无法解析索引 13 处的属性:TypedValue{t=0x2/d=0x7f010046 a=-1}

    我在 android attrs xml 文件中添加了一个用于不同色调的属性 在 styles xml 文件中 我为这些属性指定了颜色 因此每种样式的它们都不同 Attrs xml
  • 在java中是否可以使用反射创建没有无参数构造函数的“空白”类实例?

    我有一个没有默认构造函数的类 我需要一种方法来获取此类的 空白 实例 空白 意味着实例化后所有类字段都应具有默认值 如 null 0 等 我问这个问题是因为我需要能够序列化 反序列化大对象树 而且我无法访问该对象类的源 并且类既没有默认构造
  • 驱动程序信息:driver.version:未知,使用 ChromeDriver v78.0.3904.70 和 Chrome 浏览器 v78.0.3904.97

    我使用的是java 1 8和chrome浏览器版本78 0 3904 97 我正在尝试使用 chrome 驱动程序版本执行我的 selenium 脚本代码78 0 3904 70 但在执行时我面临以下问题并且 chrome 立即崩溃 Pic
  • Java 7 中 Object 和 int 的比较

    最近我偶然发现了一个问题 让我停下来思考 对我来说 下面的代码应该总是会触发错误 但是当我的一位同事问我为什么 Eclipse 没有显示错误时 我无法回答任何问题 class A public static void main String
  • 如何告诉 cxf 将包装类型保留在方法中?

    在我的 WSDL 中我有一个操作
  • wsdl 没有服务元素

    我必须使用 WCF Web 服务并获得 WSDL 外部的 因此无法控制 WSDL 在 WSDL 定义中 我没有找到包含服务 端口和地址元素的服务元素 WSDL 中不存在这种情况正常吗 这对于 WCF WSDL 来说很常见吗 我正在尝试使用轴
  • 我可以关闭并重新打开套接字吗?

    我学习了一个使用套接字的例子 在此示例中 客户端向服务器发送请求以打开套接字 然后服务器 侦听特定端口 打开套接字 一切都很好 套接字从双方 客户端和服务器 打开 但我仍然不清楚这个东西有多灵活 例如 客户端是否可以关闭一个打开的 从两端
  • 使用链接列表插入优先级队列的方法

    首先 我觉得我应该提到这是一项作业 我并不是在寻找直接的代码答案 只是为了指出正确的方向 我们被要求在链表中实现优先级队列 我正在努力编写 insert 函数的第一部分 在代码中我尝试检查是否head包含任何内容 如果没有则设置为head
  • Drools:为什么是无状态会话?

    Drools 使用会话来存储运行时数据 为此 有两种会话 无状态和有状态 与无状态会话相比 有状态会话允许迭代调用 并且似乎比无状态会话具有所有优势 那么为什么会有无状态会话呢 他们服务的目的是什么 与有状态会话相比 它们的优势是什么 谢谢
  • Java,如何管理线程读取socket(websocket)?

    我有一个 WebSocket 服务器 我的服务器创建一个新线程来处理新连接 该线程一直处于活动状态 直到 websocket 中断 我的问题 对于 1 000 000 个连接 我需要 1 000 000 个线程 我如何通过一个线程处理多个
  • 安卓框架?

    是否有任何框架比构建 Android 应用程序更容易 您会对其中一个感兴趣吗 很快就会有 我正在开发 DroidFu 一个 Android 共享库 它将为您提供 活动 和服务 中直接提供大量实用功能 例如生成列表和错误对话框 检查 Inte

随机推荐

  • TCP网络通信

    TCP与UDP都属于TCP IP协议 TCP Transmission Control Protocol 传输控制协议 是面向连接的协议 也就是说 在收发数据前 必须和对方建立可靠的连接 一个TCP连接必须要经过三次 对话 才能建立起来 其
  • python坐标轴间隔_使用Python玩转高等数学(5):三角函数

    从三角函数开始 我们使用matplotlib绘制函数图像 matplotlib是图形绘制库 使用matplotlib可以方便的绘制函数图形 以及直方图 条形图 散点图等统计图形 matplotlib的绘图命令和matlab的绘图命令基本相似
  • PHP通过OpenSSL生成证书、密钥并且加密解密数据

    转自 http www open open com code view 1421032678562 generate php
  • Interlaken协议简介

    英文原文 Interlaken Protocol Definition A Joint Specification of Cortina Systems and Cisco Systems 1 简介 网络应用中两种主流的芯片到芯片的高速网络
  • 三数之和——双指针法的实践

    一 方法介绍 双指针法有时也叫快慢指针 在数组里是用两个整型值代表下标 在链表里是两个指针 一般能实现O n 的时间解决问题 两个指针的位置一般在第一个元素和第二个元素或者第一个元素和最后一个元素 快指针在前 探路 当符合某种条件时慢指针向
  • llvm编译linux内核,linux手动编译llvm/clang

    centos 7 安装前置依赖 sudo yum install git python devel libffi devel graphviz devel elfutils libelf devel readline devel libed
  • vue2 视频下载本地,图片压缩包下载

    视频单独下载 通过url 转为blob格式的数据 下载视频 getVideoArrayBuffer url name var xhr new XMLHttpRequest xhr open GET url true xhr response
  • Tenginx UDP反向代理实现DNS服务器负载和高可用

    worker processes auto events worker connections 65535 pid var log nginx nginx pid stream upstream dns servers server 192
  • 英文中1-100表达

    数词 1 基数词 1 2 3 2 序数词 第一 第二 类型 1 2 3 4 5 6 7 8 9 10 基数词 one two three four five six seven eight nine ten 序数词 first second
  • 并发请求

    PHP并发请求种类 若干个客户机 Web 浏览器 可以同时请求同一个 PHP 解释的页面 而 Web 服务器将差不多同时返回所有这些页面 一个 Web 页面不会妨碍其他 Web 页面的发送 尽管可能会由于诸如服务器内存或网络带宽之类的受限资
  • VS 关于头文件和库文件的添加

    关于头文件和库文件的添加 一直没有很清晰的认识 下面是从网上搜索到的 自己总结的 也有一些不是很明白的 希望后续能够解决完善 也希望大神们能答疑解惑 一 头文件的添加比较清楚 一般就是 方法一 将所包含的头文件复制到当前目录 下 这个比较有
  • JDY-31蓝牙模块使用指南

    前言 本来是想买个hc 05 这种非常常用的模块 但是在优信电子买的时候 说有个可以替代的 没注意看 买回来折腾半天 这个模块是从机模块 蓝牙模块分为主机从机和主从一体的 主机与从机的区别就是 主机可以主动连接从机 但是从机不能主动连接主机
  • 11月15日 作业2,黑洞子弹,子弹发射位置朝着准星方向 UE4斯坦福 学习笔记

    黑洞子弹 这里遇到了问题 作业要求两个子弹 但是我跟着教程创建的黑洞子弹射出后不能吸引周围的物体 传送子弹不起作用 需要未来回来修改 创建一个子弹的Base 继承AActor h文件 protected Called when the ga
  • 魔兽世界不同服务器集合石显示的内容,魔兽怀旧服:详解TBC集合石机制,各副本要求不同,难怪无法使用...

    虽然在魔兽世界60级怀旧服中副本门口有集合石的存在 但是完全就是摆设 直到TBC前夕开放之后 这些集合石才真正可以使用了 然而最近一天 怀旧服中的玩家们在点击集合石的时候总是提示无法使用 还以为集合石出现了BUG 其实这并非BUG 之所以集
  • 字符串转换(蓝桥杯)

    题目描述 小蓝拥有两个字符串 S TS T 他希望通过如下操作使得字符 SS 转换为字符串 TT 操作有一下三种 删除一个字符 插入一个字符 将一个字符改为另一个字符 问最少需要操作多少次才可以使得字符串 SS 转换为字符串 TT 输入描述
  • 集成运放

    同相比例运算放大电路 信号源直接加在同相端 内阻为无穷大 缩小n倍的话就是
  • iOS开发之第三方支付支付宝支付教程,史上最新最全第三方支付宝支付方式实现、支付宝集成教程,支付宝实现流程

    本章项目demo https github com zhonggaorong alipayDemo 支付宝支付大致流程为 1 公司与支付宝进行签约 获得商户ID partner 和账号ID seller 和私钥 privateKey 开发中
  • 登录界面

  • 华为OD机试真题- 字符串解密【2023Q1】【JAVA、Python、C++】

    题目描述 给定两个字符串string1和string2 string1是一个被加扰的字符串 string1由小写英文字母 a z 和数字字符 0 9 组成 而加扰字符串由 0 9 a f 组成 string1里面可能包含0个或多个加扰子串
  • 如何使用策略模式处理多种类型请求

    1 需求简述 现在有一个活动 活动场景包含布置书籍作业 布置短文作业 布置一课一练作业 以后还可能会新增其它类型的活动 每一种活动场景有自己对应的完成逻辑和奖励 现在定义对应的场景值如下 活动名称 活动场景值 布置书籍作业 11 布置短文作