基于观察者模式设计的框架-REB,使代码模块化

设计模式里面的观察者模式,一直是作者想去设计一套框架来阐述这一个模式,因此REB(Rice Event Broker)就是为了完成观察者模式的一个框架。

观察者模式

聊REB之前,我们聊聊观察者模式带给我们特性,他能对我们框架设计提供什么好处。

什么是观察者模式
  • 观察者模式(Observer Pattern)是一种行为设计模式,用于定义对象之间的一对多依赖关系,使得一个对象的状态变化会通知其所有依赖者并自动更新它们的状态。这个模式涉及两种主要类型的对象:

    1. 被观察者:也称为主题或可观察者,是一个对象,它维护一组观察者(或依赖者)并提供方法来添加、删除和通知这些观察者。当被观察者的状态发生变化时,它会通知所有已注册的观察者。
    2. 观察者:观察者是依赖于被观察者的对象,它们实现一个接口或抽象类,包含一个更新方法(通常称为update),用于接收并处理被观察者的状态变化通知。
观察者模式工作流程
  • 被观察者注册观察者:被观察者维护一个观察者列表,并提供注册(添加)和注销(删除)观察者的方法。
  • 被观察者状态变化:当被观察者的状态发生变化,它会遍历其观察者列表,调用每个观察者的更新方法,将状态变化通知给它们。
  • 观察者响应:每个观察者在接收到通知后会执行自己的更新逻辑,以响应被观察者的状态变化。
观察者模式优势
  • **解耦性:**观察者模式可以帮助降低对象之间的耦合度。被观察者和观察者之间的关系是松散的,它们可以独立演化,而不会影响彼此的具体实现。
  • **可扩展性:**你可以轻松地添加新的观察者,而不需要修改被观察者的代码。这种扩展性使你能够动态地增加或删除观察者,以满足不同的需求。
  • **通知机制:**观察者模式允许被观察者通知观察者,从而使观察者能够在适当的时候进行响应。这可以帮助确保数据的一致性,因为观察者会立即知道被观察者的状态变化。
  • **分布式事件处理:**观察者模式常用于实现分布式事件处理系统,其中多个观察者可以远程订阅和接收事件通知。
  • **可重用性:**观察者模式可以在不同的应用中重复使用,因为它是一个通用的设计模式,不受特定应用领域的限制。
  • **灵活性:**观察者模式可以用于许多不同的场景,如用户界面更新、事件处理、数据同步等,使得代码更加灵活和可维护。
  • **支持一对多关系:**观察者模式支持一对多的依赖关系,这意味着一个被观察者可以同时通知多个观察者,从而实现多个对象之间的协同工作。
观察者模式例子
  • 物联网协议MQTT:MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议。
  • Android的EventBus:EventBus是一个基于发布者/订阅者模式的事件总线框架。

REB框架设计

REB框架图

REB框架说明
  1. REB框架分为3层:osal(OS抽象层),REB核心层(包含发布者,观察者,中间人),应用层(调用REB的模块或应用)。
  2. osal(OS抽象层):为了能让此框架应用于不同的操作系统,且不用修改框架本省,所以提供os适配层。
  3. REB核心层(包含发布者,观察者,中间人):框架的三大角色,它们三者互相依赖。
    • publisher(发布者):REB框架的发布者支持4种接口:默认发送接口,默认发送完释放数据内存释放接口,紧急发送接口,紧急发送完数据内存释放接口。
    • observer(观察者):REB框架的观察者支持3种接口:信号接收接口,回调接收接口,线程接收接口。
    • broker(中间人): REB框架的中间人支持两种接口:观察者只观察一次接口,观察者观察多次接口。
  4. 应用层(调用REB的模块或应用):上层应用或者模块,相互独立,互不依赖。
  5. REB是以事件为导向,事件类型由主事件类型和次事件类型组成,事件类型占用32个位,主事件类型占高16位,次事件类型占低16位。一般:以网络为例:主事件类型为:net_type,次事件类型为:link_up,link_down等。
REB目录结构
├─adapter
│  ├─cmsis                      
│  |   ├─reb_mutex.c            // cmsis mutex适配层
│  |   ├─reb_queue.c            // cmsis queue适配层
│  |   ├─reb_sem.c              // cmsis sem适配层
│  |   └─reb_task.c             // cmsis task适配层
│  └─rtthread                   
│      ├─reb_mutex.c            // rtthread mutex适配层
│      ├─reb_queue.c            // rtthread queue适配层
│      ├─reb_sem.c              // rtthread sem适配层
│      └─reb_task.c             // rtthread task适配层
├─example                       
│  └─reb_rtt_example.c          // rtthread 平台实例
├─include
│  ├─reb_broker.h               // reb 中间人的头文件
│  ├─reb_cfg.h                  // reb 参数配置文件
│  ├─reb_def.h                  // reb 框架通用接口定义
│  ├─reb_observer.h             // reb 观察者的头文件
│  └─reb_publisher.h            // reb 发布者的头文件
└─src├─reb_broker.c               // reb 中间人的源文件├─reb_publisher.c            // reb 观察者的源文件└─reb_observer.c             // reb 发布者的源文件

REB接口说明

broker接口
接口说明
broker_create创建broker
broker_delete删除broker
broker_observer_attach_once关联观察者到broker中,并只观察一次
broker_observer_attach关联观察者到broker中,并只观察多次
broker_observer_detach从broker中脱离观察者
  • 创建broker
    • 在使用该框架时,必须要通过此接口创建broker,它是发布者和观察的者的中间人。
reb_status broker_create(void);
参数描述
返回——
REB_OKbroker创建成功
REB_ERRORbroker创建失败
  • 删除broker
    • 当不在使用该框架时,可以调用此接口删除broker。
reb_status broker_delete(void);
参数描述
返回——
REB_OKbroker删除成功
REB_ERRORbroker删除失败
  • 关联观察者到broker中,并只观察一次
    • 我创建的观察者之后,需要通过此接口将观察者关联到broker中。当发布者发布事件,可以通过broker找到对用的观察者。使用该接口观察者只观察一次事件。
reb_status broker_observer_attach_once(observer_base *obs);
参数描述
obs观察者对象
返回——
REB_OK关联观察者到broker中,成功
REB_ERROR关联观察者到broker中,失败
  • 关联观察者到broker中,并只观察多次
    • 我创建的观察者之后,需要通过此接口将观察者关联到broker中。当发布者发布事件,可以通过broker找到对用的观察者。使用该接口观察者只观察多次事件。
reb_status broker_observer_attach(observer_base *obs);
参数描述
obs观察者对象
返回——
REB_OK关联观察者到broker中,成功
REB_ERROR关联观察者到broker中,失败
  • 从broker中脱离观察者
reb_status broker_observer_detach(observer_base *obs);
参数描述
obs观察者对象
返回——
REB_OK观察者从broker中脱离,成功
REB_ERROR观察者从broker中脱离,失败
observer接口
接口说明
observer_signal_create创建信号模式的观察者,只接收事件信号,不传输数据的观察者
observer_signal_wait信号模式的观察者,等待同步信号
observer_callback_create创建回调模式的观察者
observer_task_create创建任务模式的观察者
observer_delete删除观察者
  • 创建信号模式的观察者
    • 该接口是创建信号模式的观察者,它只接收事件信号,不传输数据的。
observer_base *observer_signal_create(uint16_t type, uint16_t sub_type);
参数描述
type观察者观察的主事件类型
sub_type观察者观察的次事件类型
返回——
obs观察者创建成功
NULL观察者创建失败
  • 信号模式的观察者,等待同步信号
    • 该接口是信号模式的观察者,用户层需要通过一个任务监听观察事件的同步信号接口。
reb_status observer_signal_wait(observer_base *base, reb_time_t timeout);
参数描述
base观察者对象
timeout观察事件的超时事件
返回——
REB_OK观察到对应事件
OTHER观察失败
  • 创建回调模式的观察者
    • 该接口是创建回调模式的观察者,当事件产生时,broker会通过回调的方式通知观察者事件的到来。
observer_base *observer_callback_create(uint16_t type,uint16_t sub_type,obs_callback_cb cb,void *arg);
参数描述
type观察者观察的主事件类型
sub_type观察者观察的次事件类型
cb事件产生时,回调的接口函数
arg回调函数的用户数据
返回——
obs观察者创建成功
NULL观察者创建失败
  • 创建任务模式的观察者
    • 该接口是创建任务模式的观察者,当事件产生时,broker会通过创建一个线程,然后由独立的线程将事件通知给观察者。
observer_base *observer_task_create(uint16_t type,uint16_t sub_type,obs_task_cb run,void *arg,uint32_t stack_size,uint32_t prio);
参数描述
type观察者观察的主事件类型
sub_type观察者观察的次事件类型
run事件产生时,线程的处理函数
arg线程处理函数的用户数据
stack_size线程的栈空间大小
prio线程的优先级
返回——
obs观察者创建成功
NULL观察者创建失败
  • 从broker中脱离观察者
reb_status observer_delete(observer_base *base);
参数描述
base观察者对象
返回——
REB_OK观察者删除成功
REB_ERROR观察者删除失败
publisher接口
接口说明
publisher_factory_create创建发布者工厂
publisher_send发布者默认发送消息
publisher_send_with_free发布者默认发送消息,发送完成之后把消息缓冲删除
publisher_urgent_send发布者发送紧急消息
publisher_urgent_send_with_free发布者发送紧急消息,发送完成之后把消息缓冲删除
  • 创建发布者工厂
    • 该接口是创建发布者工厂,提供事件队列,使发布消息处于非阻塞式发送
reb_status publisher_factory_create(pub_notify notify);
参数描述
notify事件通知回调,当发布者发布消息之后,通过回调通知broker
返回——
REB_OK发布者工厂创建成功
REB_ERROR发布者工厂创建失败
  • 发布者默认发送消息
    • 该接口是发布者发布事件接口,它是采用先进先出的方式发送消息
reb_status publisher_send(uint16_t type, uint16_t sub_type,uint32_t data, reb_time_t timeout);
参数描述
type发布消息的主事件类型
sub_type发布消息的次事件类型
data发布消息的数据
timeout发布消息的超时时间
返回——
REB_OK发布消息成功
OTHER发布消息失败
  • 发布者默认发送消息,发送完成之后把消息缓冲删除
    • 该接口是发布者发布事件接口,它是采用先进先出的方式发送消息,并且将消息发送给所有观察者之后,数据的内存会执行释放。
reb_status publisher_send_with_free(uint16_t type, uint16_t sub_type,uint32_t data, reb_time_t timeout);
参数描述
type发布消息的主事件类型
sub_type发布消息的次事件类型
data发布消息的数据
timeout发布消息的超时时间
返回——
REB_OK发布消息成功
OTHER发布消息失败
  • 发布者发送紧急消息
    • 该接口是发布者发布事件接口,它是采用插队的方式发送消息,它会将发布的消息插入消息队列的头部。
reb_status publisher_urgent_send(uint16_t type, uint16_t sub_type,uint32_t data, reb_time_t timeout);
参数描述
type发布消息的主事件类型
sub_type发布消息的次事件类型
data发布消息的数据
timeout发布消息的超时时间
返回——
REB_OK发布消息成功
OTHER发布消息失败
  • 发布者发送紧急消息,发送完成之后把消息缓冲删除
    • 该接口是发布者发布事件接口,它它是采用插队的方式发送消息,它会将发布的消息插入消息队列的头部。并且将消息发送给所有观察者之后,数据的内存会执行释放。
reb_status publisher_urgent_send_with_free(uint16_t type, uint16_t sub_type,uint32_t data, reb_time_t timeout);
参数描述
type发布消息的主事件类型
sub_type发布消息的次事件类型
data发布消息的数据
timeout发布消息的超时时间
返回——
REB_OK发布消息成功
OTHER发布消息失败

REB验证

  1. 创建三个不同模式的观察者,并关联到broker中。
  2. 通过多次不发布事件,查看观察者是否能接收到事件。
#include "rtthread.h"
#include "reb_broker.h"
#include "reb_observer.h"
#include "reb_publisher.h"observer_base *obs_signal;
observer_base *obs_call;
observer_base *obs_task;void sig_thread_handle(void *arg)                                           // 信号模式观察者监听同步信号
{while(1) {if(observer_signal_wait(obs_signal, RT_WAITING_FOREVER) == REB_OK) {rt_kprintf("signal: recv success\r\n");        }}
}void obs_callback(uint32_t event, uint32_t data, void *arg)                 // 回调模式观察者处理函数 
{rt_kprintf("call: event: 0x%08x, data: %s\r\n", event, (char *)data);
}void obs_task_fun(uint32_t event, uint32_t data, void *arg)                 // 任务模式观察者任务处理函数 
{rt_kprintf("task: event: 0x%08x, data: %s\r\n", event, (char *)data);
}int reb_init(void)
{rt_thread_t signal_thread = NULL;broker_create();                                                                        // broker创建obs_signal = observer_signal_create(1, REB_ALL_MINOR_TYPE);                             // 创建信号模式观察者signal_thread = rt_thread_create("sig_thread", sig_thread_handle, NULL, 1024, 10, 20);  // 创建线程,等待信号模式下的事件rt_thread_startup(signal_thread);obs_call = observer_callback_create(1, REB_ALL_MINOR_TYPE, obs_callback, NULL);         // 创建回调模式观察者obs_task = observer_task_create(1, REB_ALL_MINOR_TYPE, obs_task_fun, NULL, 1024, 15);   // 创建任务模式观察者broker_observer_attach(obs_signal);                                                     // 关联信号模式观察者broker_observer_attach(obs_call);                                                       // 关联回调模式观察者broker_observer_attach_once(obs_task);                                                  // 关联任务模式观察者return RT_EOK;
}
INIT_COMPONENT_EXPORT(reb_init);int reb_test(void)
{char *data = "RiceChen";publisher_send(1, 1, (int)data, 1000);          // 发布事件publisher_send(1, 2, (int)data, 1000);          // 发布事件publisher_send(1, 3, (int)data, 1000);          // 发布事件
}
MSH_CMD_EXPORT(reb_test, Rice Event broker test);

REB总结

  • REB优点:

    1. REB提供了多种的模式的观察者,可以根据需求选择不同模式的观察者。
    2. REB的发布者可以支持紧急事件发布和非紧急事件发送,保证了事件的及时响应。
    3. REB增加了OSAL层,使其不依赖于任何的平台,可以很方便的移植到其他平台。
  • REB缺点:

    1. REB的事件处理采用串行的方式,当事件积累多了,负载会比较大(后续优化)。
  • REB已经被合并到RT-THREAD软件包中,并且得到RT-THREAD技术总监的认可。开源链接:https://github.com/RiceChen0/reb

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/106742.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双十一期间如何抢占流量,打造品牌爆款产品

进入10月末,也就进入了电商行业的大促流量红利期。如何提前规划大促期间,店铺流量扩张的计划,提前抢占流量,是每一个品牌方都需要考虑的问题。今天为大家分享下双十一期间如何抢占流量,打造品牌爆款产品! 一…

Nginx请求参数解析

例: $arg_token 取的就是 uri?args 中 tokenxxx 的部分 $arg_PARAMETER #这个变量包含GET请求中,如果有变量PARAMETER时的值。$args #这个变量等于请求行中(GET请求)的参数,例如foo123&barblahblah;$binary_remote_addr #二进制的客户地…

浅谈安科瑞无线测温设备在俄罗斯某项目的应用

摘要:安科瑞ATE系列和ARTM-Pn无线测温设备适用于高低压柜的梅花触头,线缆,母排等位置对温度的实时监测。 Abstract: ATE series and ARTM-Pn are suitable for monitoring the real-time temperature of circuit breaker contact,cable,busb…

跨境电商:为民营经济注入新活力

中国的民营经济一直以来都是国家经济发展的中流砥柱,而近年来,跨境电商产业崭露头角,为民营经济注入了新的活力和机遇。本文将探讨跨境电商如何成为中国民营企业的助推引擎,以及其对民营经济的积极影响。 民营经济的支柱地位 中国…

ChatGPT AIGC 完成Excel跨多表查找操作vlookup+indirect

VLOOKUP和INDIRECT的组合在Excel中用于跨表查询,其中VLOOKUP函数用于在另一张表中查找数据,INDIRECT函数则用于根据文本字符串引用不同的工作表。具体操作如下: 1.假设在工作表1中,A列有你要查找的值,B列是你希望查询的工作表名称。 2.在工作表1的C列输入以下公式:=VLO…

iMeta框架使用方法

📢📢📢📣📣📣 哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝 一位上进心十足的【Java ToB端大厂…

判断非线性负载是否合格的方法可以从以下几个方面进行考虑:

额定功率容量:需要了解负载设备的额定功率容量,根据负载设备的规格和说明书,确定其额定功率容量是否能够满足实际需求,如果超过了负载设备的额定功率容量,可能会导致设备过载,从而影响其正常运行。 电压波形…

JVM 垃圾回收机制(可达性分析、引用计数)

目录 1 什么是垃圾2 为什么需要回收3 哪些对象被判定为垃圾呢3.1 引用计数法3.2 可达性分析算法:GC Roots根 1 什么是垃圾 垃圾是指在运行程序中没有任何指针指向的对象,就是需要被回收的。 2 为什么需要回收 执行程序会不断地分配内存空间&#xff0c…

分布式事务协调中间件---seata快速入门

分布式事务 Seata,之前叫做Fescar,是一个开源的分布式事务解决方案,它主要致力于提供高效和简单的分布式事务服务。Seata主要用于解决微服务架构下的数据一致性问题。 Seata 的基本原理是基于两阶段提交 (2PC) 以及三阶段提交 (3PC)&#xff…

nodejs+vue水浒鉴赏平台系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

C++ 反向迭代器

反向迭代器的即正向迭代器的--,反向迭代器的--即正向迭代器的,反向迭代器和正向迭代器的很多功能都是相似的,因此我们可以复用正向迭代器作为反向迭代器的底层容器来封装,从而实现出反向迭代器,即:反向迭代…

【LeetCode 算法专题突破】双指针(⭐)

文章目录 前言1. 移动零题目描述代码 2. 复写零题目描述代码 3. 快乐数题目描述代码 4. 盛最多水的容器题目描述代码 5. 有效三角形的个数题目描述代码 6. 三数之和题目描述代码 7. 四数之和题目描述代码 总结 前言 学算法入门必学的一个章节,双指针算法&#xff0…

计算机网络-计算机网络体系结构-数据链路层

目录 *一、组帧 1.1字符计数法 1.2字符填充法 1.3零比特填充法 1.4违规编码 *二、差错控制 2.1检错编码 2.2.1奇偶校验码 2.2.2 CRC循环冗余码 2.2纠错编码-海明码 *三、流量控制和可靠传输机制 流量控制 停止-等待协议 ​编辑 后退n帧协议的滑动窗口(GBN) 选择…

ChatGPT AIGC 制作大屏可视化分析案例

第一部分提示词prompt: 商品 价格 p1 13 p2 41 p3 42 p4 53 p5 19 p6 28 p7 92 p8 62 城市 销量 北京 69 上海 13 南京 18 武汉 66 成都 70 你现在是一名非常专业的数据分析师,请结合上述数据完成下列几件事情 1:第一部分数…

基于 Triple 实现 Web 移动端后端全面打通

*作者:陈有为,陌陌研发工程师、Apache Dubbo PMC RPC 协议开发微服务 在我们正常开发微服务的时候,传统 RPC 服务可能在最底层。上层可能是浏览器、移动端、外界的服务器、自己的测试、curl 等等。我们可能会通过 Tomcat 这种外部服务器去组…

机器视觉知识讲的深不如讲的透

我深思这个话题,大家来培训,其实培训机构也很痛苦,每个热掌握的参差不齐,你说他不会吧,会一点电气,你说他会吧,会一点Opencv,会一点visionpro,会一点Visionmaster,会一点Halcon。好像…

【Retinex theory】【图像增强】-笔记

1 前言 retinex 是常见的图像增强的方法,retinex 是由两个单词合成的:retina conrtex ,即视网膜皮层。 2 建立的基础 Land 的 retinex theory 建立在三个假设之下: 真实世界是无色的,我们所谓的颜色是光和物质相互…

上位机在自动化中有何作用和优势?

今日话题 上位机在自动化中有何作用和优势? 自动化控制编程领域包括单片机、PLC、机器视觉和运动控制等方向。输入“777”,即刻获取关于上位机开发和数据可视化的专业学习资料,近年来,上位机编程逐渐兴起,正在逐步替…

【Linux】环境下部署Nginx服务 - 二进制部署方式

👨‍🎓博主简介 🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支…

Linux网络编程系列之服务器编程——非阻塞IO模型

Linux网络编程系列 (够吃,管饱) 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编…