大家好,我是雜燴君。
本次我們分享的是嵌入式中常用的一種思想 / 編程模型——觀察者模式。
觀察者模式概述
觀察者模式(Observer Pattern)是一種行為設(shè)計(jì)模式,其核心在于建立對象間的動態(tài)訂閱-通知機(jī)制。
它定義了對象之間的一對多依賴關(guān)系,當(dāng)一個對象(被觀察對象,也稱為主題)的狀態(tài)發(fā)生變化時,所有依賴它的對象(觀察者)都會收到通知并自動更新。
在嵌入式系統(tǒng)中,觀察者模式廣泛應(yīng)用于解耦事件發(fā)布者與訂閱者,特別適合的應(yīng)用場景:
- 處理傳感器數(shù)據(jù)更新
- 硬件狀態(tài)變化
- 多模塊協(xié)作
嵌入式應(yīng)用場景
1、傳感器數(shù)據(jù)分發(fā)
多個模塊(如顯示、存儲、報(bào)警)需要實(shí)時獲取傳感器數(shù)據(jù)變化,觀察者模式可將傳感器作為主題(Subject),各模塊作為觀察者(Observer),實(shí)現(xiàn)數(shù)據(jù)更新時的自動通知。
類圖:
主題類(SensorSubject):
- 包含觀察者列表(observers數(shù)組)
- 維護(hù)當(dāng)前傳感器值(sensor_value)
- 提供attach和set_value兩個關(guān)鍵方法
觀察者接口(ObserverCallback):
- 定義統(tǒng)一的update接口
- 對應(yīng)代碼中的函數(shù)指針類型
具體觀察者類:
- DisplayObserver:處理顯示更新
- LoggerObserver:處理日志記錄
- AlarmObserver:處理閾值報(bào)警
代碼:
#include
#define OBSERVER_MAX_NUM 5
// 觀察者回調(diào)函數(shù)類型
typedefvoid(*ObserverCallback)(int value);
// 主題(被觀察者)
typedefstruct
{
ObserverCallback observers[OBSERVER_MAX_NUM];
int count;
int sensor_value;
} SensorSubject;
// 附加觀察者到主題
voidsensor_attach(SensorSubject* subject, ObserverCallback callback)
{
if (!subject || !callback)
{
printf("Invalid parameters!\n");
return;
}
if (subject->count >= OBSERVER_MAX_NUM)
{
printf("Observers full!\n");
return;
}
subject->observers[subject->count++] = callback;
}
// 更新傳感器值并通知觀察者
voidsensor_set_value(SensorSubject* subject, int value)
{
if (!subject)
{
printf("Invalid parameters!\n");
return;
}
subject->sensor_value = value;
// 遍歷所有觀察者進(jìn)行通知
for (int i = 0; i < subject->count; ++i)
{
if (subject->observers[i])
{
subject->observers[i](subject->sensor_value);
}
}
}
// 觀察者1:顯示模塊
voiddisplay_update(int value)
{
printf("[Display] Value: %d\n", value);
}
// 觀察者2:日志模塊回
voidlogger_update(int value)
{
printf("[Logger] Value: %d\n", value);
}
// 觀察者3:報(bào)警模塊
voidalarm_update(int value)
{
if (value > 100)
{
printf("[Alarm] Value %d exceeds limit!\n", value);
}
}
intmain(void)
{
// 初始化傳感器主題
SensorSubject sensor =
{
.observers = {0},
.count = 0,
.sensor_value = 0
};
// 注冊觀察者
sensor_attach(&sensor, display_update);
sensor_attach(&sensor, logger_update);
sensor_attach(&sensor, alarm_update);
// 模擬傳感器數(shù)據(jù)更新
sensor_set_value(&sensor, 25);
sensor_set_value(&sensor, 120);
return0;
}
這個例子允許對象(顯示、日志、報(bào)警模塊)訂閱另一個對象(傳感器),當(dāng)主題狀態(tài)變化時自動通知所有觀察者。
注意:這個例子只是為了解釋觀察者模式的基本思想,在單線程環(huán)境下基本實(shí)現(xiàn)了觀察者模式的核心功能。若需要模仿應(yīng)用于實(shí)際應(yīng)用,需要增加線程安全機(jī)制、動態(tài)內(nèi)存管理、更完善的錯誤處理等。
2、Zephyr傳感器子系統(tǒng)
在 Zephyr 中,傳感器子系統(tǒng)使用了類似觀察者模式的機(jī)制。傳感器驅(qū)動作為主題,當(dāng)傳感器數(shù)據(jù)更新時,會觸發(fā)相應(yīng)的事件。
而應(yīng)用程序可以注冊為觀察者,監(jiān)聽這些事件并在數(shù)據(jù)更新時進(jìn)行處理。
#include
#include
#include
#include
// 傳感器事件處理函數(shù),作為觀察者的更新方法
staticvoidsensor_callback(const struct device *dev, struct sensor_trigger *trig)
{
structsensor_valuetemp;
if (sensor_sample_fetch(dev) < 0) {
return;
}
if (sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp) < 0) {
return;
}
// 處理傳感器數(shù)據(jù)
printk("Temperature: %d.%06d\n", temp.val1, temp.val2);
}
voidmain(void)
{
conststructdevice *dev = device_get_binding(DT_LABEL(DT_INST(0, st_stts751)));
if (dev == NULL) {
return;
}
structsensor_triggertrig = {
.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_AMBIENT_TEMP
};
// 注冊傳感器事件回調(diào),相當(dāng)于注冊觀察者
if (sensor_trigger_set(dev, &trig, sensor_callback) < 0) {
return;
}
while (1) {
k_sleep(K_MSEC(100));
}
}
3、任務(wù)間通信和同步機(jī)制
在 RTOS 中,任務(wù)之間的通信和同步機(jī)制可以類比為觀察者模式。
EventGroupHandle_t xEventGroup;
// 創(chuàng)建事件組
xEventGroup = xEventGroupCreate();
// 任務(wù) 1 作為主題設(shè)置事件
voidvTask1( void *pvParameters )
{
while(1)
{
// 設(shè)置事件位
xEventGroupSetBits( xEventGroup, 0x01 );
vTaskDelay( pdMS_TO_TICKS( 1000 ) );
}
}
// 任務(wù) 2 作為觀察者等待事件
voidvTask2( void *pvParameters )
{
EventBits_t uxBits;
while(1)
{
// 等待事件位
uxBits = xEventGroupWaitBits(
xEventGroup, // 事件組句柄
0x01, // 等待的事件位
pdTRUE, // 退出時清除事件位
pdFALSE, // 不需要所有位都設(shè)置
portMAX_DELAY // 無限期等待
);
// 處理事件
if( ( uxBits & 0x01 ) != 0 )
{
// 執(zhí)行相應(yīng)操作
}
}
}
事件組(Event Group)就可以看作是一個主題,而等待這些事件的任務(wù)則可以看作是觀察者。
當(dāng)事件組中的某個事件被設(shè)置(狀態(tài)改變)時,等待該事件的任務(wù)會被喚醒并執(zhí)行相應(yīng)的操作,就如同觀察者接收到主題的通知后進(jìn)行更新一樣。
4、MQTT
MQTT 是一種輕量級的消息傳輸協(xié)議,主要用于物聯(lián)網(wǎng)設(shè)備之間的通信,其在設(shè)計(jì)和使用上應(yīng)用了觀察者模式的思想。
其核心概念包括:
- 發(fā)布者(Publisher):產(chǎn)生消息并將其發(fā)布到特定的主題(Topic)。
- 主題(Topic):消息的分類標(biāo)簽,用于區(qū)分不同類型的消息。
- 代理(Broker):負(fù)責(zé)接收發(fā)布者的消息,并將消息轉(zhuǎn)發(fā)給訂閱了相應(yīng)主題的訂閱者。
- 訂閱者(Subscriber):訂閱一個或多個主題,當(dāng)這些主題有新消息發(fā)布時,會收到代理轉(zhuǎn)發(fā)的消息。
以上就是本次的分享,如果覺得文章有幫助,麻煩幫忙轉(zhuǎn)發(fā),謝謝!