图示

1. 现状

场景: 假设设备上报不同类型的消息,我们要对不同类型的消息做不同的处理。如果我们通过if..else的方式处理的话会显得比较冗余。

例如:

if("alarmEvent".equals(msg)){
// 处理告警消息逻辑 ...
}else if("deviceBase".equals(msg)){
// 处理设备上报的基本信息 ...
}else if("heartBeat".equals(msg)){
// 处理设备心跳消息 ...
}else {
// ...
}

2. 消息处理Handler

那么对于不同消息的不同的处理逻辑我们可以单独放在一个实现类中,这些类有着相同的行为,所以我们可以定义一个接口:

public interface MessageHandler<T>{
T invoke(String bizUpMessage); // 处理接收到的消息
String getName();
String getIdentification(); // 获取消息标志
}

针对于不同的消息,我们可以有各自的实现

实现类1:

@Component
public class DeviceBaseInfoHandler implements MessageHandler<String> { @Override
public String invoke(String bizUpMessage) {
// 处理设备上报的基本信息 ...
return "";
} @Override
public String getName() {
return "";
} @Override
public String getIdentification() {
return "DeviceBaseInfoRequest";
}
}

实现类2:

@Component
public class AlarmEventHandler implements MessageHandler<String>{ @Override
public String invoke(String bizUpMessage) {
// 处理告警信息 ...
return null;
} @Override
public String getName() {
return "";
} @Override
public String getIdentification() {
return "AlarmEventRequest";
}
}

3. Handler注册处

我们可以将上一步的消息处理对象,根据消息的标志(Identification)不同,将消息放在一个Map中:

public class MessageHandlerRegisty {

    private Map<String, List<MessageHandler>> registerMap = new HashMap<>();

    // 注册各种 handler
public void regist(String identification, MessageHandler handler){
if(this.contains(identification)){
get(identification).add(handler);
} else {
List<MessageHandler> list = new ArrayList<>();
list.add(handler);
registerMap.put(identification, list);
}
} public boolean contains(String identification){
return registerMap.containsKey(identification);
} public List<MessageHandler> get(String identification){
return registerMap.get(identification);
}
}

4. 消息接收Service

消息接收接口

public interface IMessageService {
void messageReceived(String bizUpMessage); // 接收消息
}

5. 消息接收ServiceImpl

消息接收实现类

public class MessageServiceImpl implements IMessageService{

    public MessageHandlerRegisty registy;

    // 线程池
public ExecutorService executorService = new ThreadPoolExecutor(3, 10, 15, TimeUnit.MINUTES, new LinkedBlockingQueue(1000), new LogdDiscardPolicy()); public void messageReceived(String bizUpMessage){ // 获取消息的标志Identification
String identification = extractIdentification(bizUpMessage); if(StringUtils.isBlank(identification)){
return;
} if(!registy.contains(identification)){
return;
} for(MessageHandler handler : registy.get(identification)){
execute(handler, bizUpMessage); // 多线程处理消息
}
} public MessageHandlerRegisty getRegisty() {
return registy;
}
public void setRegisty(MessageHandlerRegisty registy) {
this.registy = registy;
} private void execute(MessageHandler handler, String bizUpMessage){
executorService.submit(new IotMessageTask(handler, bizUpMessage));
} public final class LogdDiscardPolicy extends ThreadPoolExecutor.DiscardPolicy {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
log.warn("MessageService pool is full !!!!!!!!");
}
} final class IotMessageTask implements Runnable { private MessageHandler handler;
private String bizUpMessage; public IotMessageTask(MessageHandler handler, String bizUpMessage){
this.handler = handler;
this.bizUpMessage = bizUpMessage;
} @Override
public void run() {
try {
Stopwatch stopwatch = Stopwatch.createStarted();
handler.invoke(bizUpMessage);
stopwatch = stopwatch.stop(); long cost = stopwatch.elapsed(TimeUnit.MICROSECONDS);
logTime(cost);
}catch (Exception e){
log.error("handler execute error - ", e);
}
} private void logTime(long cost){
if(cost > 50){
log.warn(" handler -> {}, cost too much -> {}", handler.getName(), cost);
} else {
log.info("handler -> {}, cost -> {}", handler.getName(), cost);
}
} } }

6. 将MessageServiceImpl对象配置到spring容器

@Configuration
public class ManagerConfig { @Bean
public IMessageService messageService(){
MessageServiceImpl messageService = new MessageServiceImpl();
messageService.setRegisty(buildRegisty());
return messageService;
} // 消息处理handler
@Autowired
private DeviceBaseInfoHandler deviceBaseInfoHandler;
@Autowired
private AlarmEventHandler alarmEventHandler; private MessageHandlerRegisty buildRegisty(){
MessageHandlerRegisty registy = new MessageHandlerRegisty();
registy.regist(deviceBaseInfoHandler.getIdentification(), deviceBaseInfoHandler);
registy.regist(alarmEventHandler.getIdentification(),alarmEventHandler); return registy;
}
}

策略模式+Spring配置类优化多if..else思路的更多相关文章

  1. Spring配置类理解(Lite模式和Full模式)

    Spring中的注解大家在开发中都常常用到,但是如果我说我们常常用的注解修饰了一个类,表示这个类就是一个配置类很多开发者都会有点迷糊了.具体配置类在开发中有什么样的用途我就不多说了,相信很多人都知道, ...

  2. Spring配置类为什么要分Full和Lite模式

    本文基于Spring 5.2.15-RELEASE 关于Spring配置类的Full模式和Lite模式,如果没有仔细阅读过源码或者官方文档的话,估计很多人都不知道这个概念.所以我们先来解释下这两个概念 ...

  3. Spring配置类深度剖析-总结篇(手绘流程图,可白嫖)

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  4. 使用spring配置类代替xml配置文件注册bean类

    spring配置类,即在类上加@Configuration注解,使用这种配置类来注册bean,效果与xml文件是完全一样的,只是创建springIOC容器的方式不同: //通过xml文件创建sprin ...

  5. 521我发誓读完本文,再也不会担心Spring配置类问题了

    当大潮退去,才知道谁在裸泳.关注公众号[BAT的乌托邦]开启专栏式学习,拒绝浅尝辄止.本文 https://www.yourbatman.cn 已收录,里面一并有Spring技术栈.MyBatis.中 ...

  6. 简单工厂模式和策略模式结合使用php

    策略模式是有客户端自行实例化算法类的,而简单工厂模客户端只传参数,不关心对象的生成. 结合两种模式,可以在使用策略模式的时候客户端不再生成算法的对象.修改策略模式的配置类即可. 在之前策略模式基础上, ...

  7. 真懂Spring的@Configuration配置类?你可能自我感觉太良好

    当大潮退去,才知道谁在裸泳.关注公众号[BAT的乌托邦]开启专栏式学习,拒绝浅尝辄止.本文 https://www.yourbatman.cn 已收录,里面一并有Spring技术栈.MyBatis.中 ...

  8. 使用配置类而不使用XML文件(代替bean.xml)对spring进行配置

    以下类是一个配置类,它的作用和bean.xml是一样的注解: @Configuration 作用: 用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解. 获取容器时需要使用Anno ...

  9. 【Spring】简述@Configuration配置类注册BeanDefinition到Spring容器的过程

    概述 本文以SpringBoot应用为基础,尝试分析基于注解@Configuration的配置类是如何向Spring容器注册BeanDefinition的过程 其中主要分析了 Configuratio ...

  10. 【LabVIEW技巧】策略模式

    前言 在之前的文章提到了如何学习OOP以及对应的简单工厂模式,由于时间比较长,我们先回顾一下原有内容,然后继续了解新的模式. 为什么学习OOP 在测控系统的软件开发过程中,LabVIEW工程师一直认为 ...

随机推荐

  1. Rust -- 模式与匹配

    1. 模式 用来匹配类型中的结构(数据的形状),结合 模式和match表达式 提供程序控制流的支配权 模式组成内容 字面量 解构的数组.枚举.结构体.元祖 变量 通配符 占位符 流程:匹配值 --&g ...

  2. 深度学习实现案例(Tensorflow、PaddlePaddle)

    深度学习实验案例 文章目录 深度学习实验案例 一.基础理论 实验一:自定义感知机 实验二:验证图像卷积运算效果 二.Tensorflow 实验一:查看Tensorflow版本 实验二:Hellowor ...

  3. blob转string,同步调用

    问题背景 通过接口下载文件的时候,后端设置的responseHeader content-disposition: attachment;filename=文件名.xlsx content-type: ...

  4. 2021-12-26:给定一个长度为n的数组arr,求有多少个子数组满足 : 子数组两端的值,是这个子数组的最小值和次小值,最小值和次小值谁在最左和最右无所谓。 n<=100000(10^5) n*

    2021-12-26:给定一个长度为n的数组arr,求有多少个子数组满足 : 子数组两端的值,是这个子数组的最小值和次小值,最小值和次小值谁在最左和最右无所谓. n<=100000(10^5) ...

  5. mac系统下,docker安装kibana报错,manifest for kibana:latest not found: manifest unknown: manifest unknown

    1.问题描述:mac系统下,docker安装kibana报错,manifest for kibana:latest not found: manifest unknown: manifest unkn ...

  6. 2021-09-28:合并区间。以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠

    2021-09-28:合并区间.以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] .请你合并所有重叠的区间,并返回一个不重叠 ...

  7. .NET6项目连接数据库方式方法

    前言 接上一篇Linux系统下创建dotnet项目,这一篇我们聊聊.NET6环境下dotnet项目连接数据库的方式方法,包括数据库字符串该如何配置.看了很多博主写的文章,连接数据库字符串配置的方式和位 ...

  8. dotnet平台Http消息处理者工厂

    1 前言 Microsoft.Extensions.Http是一个设计非常优异的客户端工厂库,其提供了IHttpClientFactory用于创建HttpClient和IHttpMessageHand ...

  9. kafka生产者你不得不知的那些事儿

    前言 kafka生产者作为消息发送中很重要的一环,这里面可是大有文章,你知道生产者消息发送的流程吗?知道消息是如何发往哪个分区的吗?如何保证生产者消息的可靠性吗?如何保证消息发送的顺序吗?如果对于这些 ...

  10. GPT虚拟直播Demo系列(二)|无人直播间实现虚拟人回复粉丝

    摘要 虚拟人和数字人是人工智能技术在现实生活中的具体应用,它们可以为人们的生活和工作带来便利和创新.在直播间场景里,虚拟人和数字人可用于直播主播.智能客服.营销推广等.接入GPT的虚拟人像是加了超强b ...