0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

Rice嵌入式开发技术分享 来源:Rice 嵌入式开发技术分享 作者:Rice 嵌入式开发技 2023-10-17 09:35 次阅读

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

观察者模式

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

什么是观察者模式

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

被观察者:也称为主题或可观察者,是一个对象,它维护一组观察者(或依赖者)并提供方法来添加、删除和通知这些观察者。当被观察者的状态发生变化时,它会通知所有已注册的观察者。

观察者:观察者是依赖于被观察者的对象,它们实现一个接口或抽象类,包含一个更新方法(通常称为update),用于接收并处理被观察者的状态变化通知。

观察者模式工作流程

被观察者注册观察者:被观察者维护一个观察者列表,并提供注册(添加)和注销(删除)观察者的方法。

被观察者状态变化:当被观察者的状态发生变化,它会遍历其观察者列表,调用每个观察者的更新方法,将状态变化通知给它们。

观察者响应:每个观察者在接收到通知后会执行自己的更新逻辑,以响应被观察者的状态变化。

观察者模式优势

「解耦性:」观察者模式可以帮助降低对象之间的耦合度。被观察者和观察者之间的关系是松散的,它们可以独立演化,而不会影响彼此的具体实现。

「可扩展性:」你可以轻松地添加新的观察者,而不需要修改被观察者的代码。这种扩展性使你能够动态地增加或删除观察者,以满足不同的需求。

「通知机制:」观察者模式允许被观察者通知观察者,从而使观察者能够在适当的时候进行响应。这可以帮助确保数据的一致性,因为观察者会立即知道被观察者的状态变化。

「分布式事件处理:」观察者模式常用于实现分布式事件处理系统,其中多个观察者可以远程订阅和接收事件通知。

「可重用性:」观察者模式可以在不同的应用中重复使用,因为它是一个通用的设计模式,不受特定应用领域的限制。

「灵活性:」观察者模式可以用于许多不同的场景,如用户界面更新、事件处理、数据同步等,使得代码更加灵活和可维护。

「支持一对多关系:」观察者模式支持一对多的依赖关系,这意味着一个被观察者可以同时通知多个观察者,从而实现多个对象之间的协同工作。

观察者模式例子

物联网协议MQTT:MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议。

Android的EventBus:EventBus是一个基于发布者/订阅者模式的事件总线框架。

REB框架设计

REB框架图

9c0afb10-6c0f-11ee-b3e3-92fbcf53809c.png

REB框架说明

REB框架分为3层:osal(OS抽象层),REB核心层(包含发布者,观察者,中间人),应用层(调用REB的模块或应用)。

osal(OS抽象层):为了能让此框架应用于不同的操作系统,且不用修改框架本身,所以提供os适配层。

REB核心层(包含发布者,观察者,中间人):框架的三大角色,它们三者互相依赖。

publisher(发布者):REB框架的发布者支持4种接口:默认发送接口,默认发送完释放数据内存释放接口,紧急发送接口,紧急发送完数据内存释放接口。

observer(观察者):REB框架的观察者支持3种接口:信号接收接口,回调接收接口,线程接收接口。

broker(中间人):REB框架的中间人支持两种接口:观察者只观察一次接口,观察者观察多次接口。

应用层(调用REB的模块或应用):上层应用或者模块,相互独立,互不依赖。

REB是以事件为导向,事件类型由主事件类型和次事件类型组成,事件类型占用32个位,主事件类型占高16位,次事件类型占低16位。一般:以网络为例:主事件类型为:net_type,次事件类型为:link_up,link_down等。

REB目录结构

├─adapter
│├─cmsis
│|├─reb_mutex.c//cmsismutex适配层
│|├─reb_queue.c//cmsisqueue适配层
│|├─reb_sem.c//cmsissem适配层
│|└─reb_task.c//cmsistask适配层
│└─rtthread
│├─reb_mutex.c//rtthreadmutex适配层
│├─reb_queue.c//rtthreadqueue适配层
│├─reb_sem.c//rtthreadsem适配层
│└─reb_task.c//rtthreadtask适配层
├─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_statusbroker_create(void);

「参数」 「描述」
-- --
「返回」 ——
REB_OK broker创建成功
REB_ERROR broker创建失败

删除broker

当不再使用该框架时,可以调用此接口删除broker。

reb_statusbroker_delete(void);

「参数」 「描述」
-- --
「返回」 ——
REB_OK broker删除成功
REB_ERROR broker删除失败

关联观察者到broker中,并只观察一次

我创建的观察者之后,需要通过此接口将观察者关联到broker中。当发布者发布事件,可以通过broker找到对用的观察者。使用该接口观察者只观察一次事件。

reb_statusbroker_observer_attach_once(observer_base*obs);

「参数」 「描述」
obs 观察者对象
「返回」 ——
REB_OK 关联观察者到broker中,成功
REB_ERROR 关联观察者到broker中,失败

关联观察者到broker中,并只观察多次

我创建的观察者之后,需要通过此接口将观察者关联到broker中。当发布者发布事件,可以通过broker找到对用的观察者。使用该接口观察者只观察多次事件。

reb_statusbroker_observer_attach(observer_base*obs);

「参数」 「描述」
obs 观察者对象
「返回」 ——
REB_OK 关联观察者到broker中,成功
REB_ERROR 关联观察者到broker中,失败

从broker中脱离观察者

reb_statusbroker_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_ttype,uint16_tsub_type);

「参数」 「描述」
type 观察者观察的主事件类型
sub_type 观察者观察的次事件类型
「返回」 ——
obs 观察者创建成功
NULL 观察者创建失败

信号模式的观察者,等待同步信号

该接口是信号模式的观察者,用户层需要通过一个任务监听观察事件的同步信号接口。

reb_statusobserver_signal_wait(observer_base*base,reb_time_ttimeout);

「参数」 「描述」
base 观察者对象
timeout 观察事件的超时事件
「返回」 ——
REB_OK 观察到对应事件
OTHER 观察失败

创建回调模式的观察者

该接口是创建回调模式的观察者,当事件产生时,broker会通过回调的方式通知观察者事件的到来。

observer_base*observer_callback_create(uint16_ttype,
uint16_tsub_type,
obs_callback_cbcb,
void*arg);

「参数」 「描述」
type 观察者观察的主事件类型
sub_type 观察者观察的次事件类型
cb 事件产生时,回调的接口函数
arg 回调函数的用户数据
「返回」 ——
obs 观察者创建成功
NULL 观察者创建失败

创建任务模式的观察者

该接口是创建任务模式的观察者,当事件产生时,broker会通过创建一个线程,然后由独立的线程将事件通知给观察者。

observer_base*observer_task_create(uint16_ttype,
uint16_tsub_type,
obs_task_cbrun,
void*arg,
uint32_tstack_size,
uint32_tprio);

「参数」 「描述」
type 观察者观察的主事件类型
sub_type 观察者观察的次事件类型
run 事件产生时,线程的处理函数
arg 线程处理函数的用户数据
stack_size 线程的栈空间大小
prio 线程的优先级
「返回」 ——
obs 观察者创建成功
NULL 观察者创建失败

从broker中脱离观察者

reb_statusobserver_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_statuspublisher_factory_create(pub_notifynotify);

「参数」 「描述」
notify 事件通知回调,当发布者发布消息之后,通过回调通知broker
「返回」 ——
REB_OK 发布者工厂创建成功
REB_ERROR 发布者工厂创建失败

发布者默认发送消息

该接口是发布者发布事件接口,它是采用先进先出的方式发送消息

reb_statuspublisher_send(uint16_ttype,uint16_tsub_type,
uint32_tdata,reb_time_ttimeout);

「参数」 「描述」
type 发布消息的主事件类型
sub_type 发布消息的次事件类型
data 发布消息的数据
timeout 发布消息的超时时间
「返回」 ——
REB_OK 发布消息成功
OTHER 发布消息失败

发布者默认发送消息,发送完成之后把消息缓冲删除

该接口是发布者发布事件接口,它是采用先进先出的方式发送消息,并且将消息发送给所有观察者之后,数据的内存会执行释放。

reb_statuspublisher_send_with_free(uint16_ttype,uint16_tsub_type,
uint32_tdata,reb_time_ttimeout);

「参数」 「描述」
type 发布消息的主事件类型
sub_type 发布消息的次事件类型
data 发布消息的数据
timeout 发布消息的超时时间
「返回」 ——
REB_OK 发布消息成功
OTHER 发布消息失败

发布者发送紧急消息

该接口是发布者发布事件接口,它是采用插队的方式发送消息,它会将发布的消息插入消息队列的头部。

reb_statuspublisher_urgent_send(uint16_ttype,uint16_tsub_type,
uint32_tdata,reb_time_ttimeout);

「参数」 「描述」
type 发布消息的主事件类型
sub_type 发布消息的次事件类型
data 发布消息的数据
timeout 发布消息的超时时间
「返回」 ——
REB_OK 发布消息成功
OTHER 发布消息失败

发布者发送紧急消息,发送完成之后把消息缓冲删除

该接口是发布者发布事件接口,它它是采用插队的方式发送消息,它会将发布的消息插入消息队列的头部。并且将消息发送给所有观察者之后,数据的内存会执行释放。

reb_statuspublisher_urgent_send_with_free(uint16_ttype,uint16_tsub_type,
uint32_tdata,reb_time_ttimeout);

「参数」 「描述」
type 发布消息的主事件类型
sub_type 发布消息的次事件类型
data 发布消息的数据
timeout 发布消息的超时时间
「返回」 ——
REB_OK 发布消息成功
OTHER 发布消息失败

REB验证

创建三个不同模式的观察者,并关联到broker中。

通过多次不发布事件,查看观察者是否能接收到事件。

#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;

voidsig_thread_handle(void*arg)//信号模式观察者监听同步信号
{
while(1){
if(observer_signal_wait(obs_signal,RT_WAITING_FOREVER)==REB_OK){
rt_kprintf("signal:recvsuccessrn");
}
}
}

voidobs_callback(uint32_tevent,uint32_tdata,void*arg)//回调模式观察者处理函数
{
rt_kprintf("call:event:0x%08x,data:%srn",event,(char*)data);
}

voidobs_task_fun(uint32_tevent,uint32_tdata,void*arg)//任务模式观察者任务处理函数
{
rt_kprintf("task:event:0x%08x,data:%srn",event,(char*)data);
}

intreb_init(void)
{
rt_thread_tsignal_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);//关联任务模式观察者

returnRT_EOK;
}
INIT_COMPONENT_EXPORT(reb_init);

intreb_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,RiceEventbrokertest);
9c1e5e4e-6c0f-11ee-b3e3-92fbcf53809c.jpg

REB总结

REB优点:

REB提供了多种的模式的观察者,可以根据需求选择不同模式的观察者。

REB的发布者可以支持紧急事件发布和非紧急事件发送,保证了事件的及时响应。

REB增加了OSAL层,使其不依赖于任何的平台,可以很方便的移植到其他平台。

REB缺点:

REB的事件处理采用串行的方式,当事件积累多了,负载会比较大(后续优化)。

REB已经被合并到RT-THREAD软件包中,并且得到RT-THREAD技术总监的认可。开源链接:https://github.com/RiceChen0/reb

9c3414c8-6c0f-11ee-b3e3-92fbcf53809c.png  

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 框架
    +关注

    关注

    0

    文章

    403

    浏览量

    17477
  • 模块化
    +关注

    关注

    0

    文章

    331

    浏览量

    21344
  • 代码
    +关注

    关注

    30

    文章

    4780

    浏览量

    68531
收藏 人收藏

    评论

    相关推荐

    为什么要开发模块化测试框架

    为什么要开发模块化测试框架?具体该怎么做?
    发表于 08-08 06:06

    CC2540广播角色和观察者角色切换代码怎么编写?

    希望一个CC2540先通过观察者角色获取其他广播的广播数据,然后在切换为广播角色将这些数据广播给另外一个观察者?这样就需要编程实现观察者
    发表于 03-16 10:27

    RN4020观察者模式无法正常工作怎么回事

    中心,支持MLDP,并使UART流控制R,1//重新引导,使更改生效J,1//观察者模式你对这个问题有什么想法?谢谢,弗朗西斯科
    发表于 04-22 09:03

    属性观察者的特点

    属性观察者,类似于触发器。用来监视属性的除初始之外的属性值变化,当属性值发生改变时可以对此作出响应。有如下特点: 1,不仅可以在属性值改变后触发didSet,也可以在属性值改变前触发willSet
    发表于 11-04 07:10

    观察者模式在嵌入式编程设计中有何作用

    观察者模式是最常见的模式之一。这种模式提供一种方法来时对象“监听”其他对象,而不需要修改任何数据服务器。在嵌入式领域,这意味着数据能够很容易分享给其他元素。
    发表于 12-22 08:31

    基于观察者模式的屏幕布局控件设计

    观察者模式作为设计模式中行为模式的一种,解决了上述具有一对多依赖关系对象重用问题。文中在分析观察者模式
    发表于 02-13 16:20 4次下载
    基于<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>的屏幕布局控件设计

    全面提升自动测试,NI TestStand 2012新模块化框架

    NI TestStand 2012采用全新的模块化框架,能够简化自动测试的开发和报告生成。
    发表于 11-06 17:42 1778次阅读

    Java设计模式分析之观察者

    观察者模式的流程跟报纸订阅方式一致,即:观察者模式=出版+订阅,只是名称不一样,出版
    发表于 09-26 17:36 0次下载

    在 Java8 环境下实现观察者模式的实例分析

    观察者(Observer)模式又名发布-订阅(Publish/Subscribe)模式,是四人组(GoF,即 Erich Gamma、Richard Helm、Ralph Johnson
    发表于 10-12 16:09 0次下载
    在 Java8 环境下实现<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>的实例分析

    基于AT32(STM32)单片机的模块化代码之——按键代码模块化

    基于AT32(STM32)单片机的模块化代码之按键模块化1.环境介绍平台:AT32F415单片机,雅特力公司的AT32系列单片机其实跟STM32系列单片机大同小异,包括库函数等基本都是一样的,所以
    发表于 11-19 10:06 37次下载
    基于AT32(STM32)单片机的<b class='flag-5'>模块化</b><b class='flag-5'>代码</b>之——按键<b class='flag-5'>代码</b><b class='flag-5'>模块化</b>

    基于AT32(STM32)单片机的模块化代码之——ADC代码模块化

    基于AT32(STM32)单片机的模块化代码之——ADC代码模块化1.环境介绍平台:AT32F415单片机,雅特力公司的AT32系列单片机其实跟STM32系列单片机大同小异,包括库函数
    发表于 11-19 10:36 17次下载
    基于AT32(STM32)单片机的<b class='flag-5'>模块化</b><b class='flag-5'>代码</b>之——ADC<b class='flag-5'>代码</b><b class='flag-5'>模块化</b>

    设计模式行为型:观察者模式

    定义对象之间的一种一对多依赖关系,使得每一个对象发生状态的变化时,其相关依赖对象皆得到通知并被自动更新,又称为发布-订阅模式、模型-视图模式、源-监听器模式或从属
    的头像 发表于 06-07 16:56 667次阅读
    设计<b class='flag-5'>模式</b>行为型:<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>

    观察者模式,超详细!

    观察者模式建议你为发布类添加订阅机制, 让每个对象都能订阅或取消订阅发布事件流。 不要害怕! 这并不像听上去那么复杂。 实际上, 该机制包括 1) 一个用于存储订阅
    的头像 发表于 08-21 16:06 1196次阅读
    <b class='flag-5'>观察者</b><b class='flag-5'>模式</b>,超详细!

    一文解析BLE观察者模式回调机制

    nRF5 SDK从版本14开始,对事件回调机制做了更新,引入了观察者模式,以解耦不同BLE Layer对BLE事件的回调函数。
    的头像 发表于 11-27 10:07 997次阅读
    一文解析BLE<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>回调机制

    什么是观察者设计模式?Golang中的观察者模式介绍

    当涉及到订单处理系统时,观察者设计模式可以用于实现订单状态的变化和通知。
    的头像 发表于 01-08 10:08 422次阅读