• 正文
    • 觀察者模式概述
    • 嵌入式應用場景
  • 相關推薦
申請入駐 產(chǎn)業(yè)圖譜

嵌入式編程模型 | 觀察者模式

03/16 10:55
1033
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是雜燴君。本次我們分享的是嵌入式中常用的一種思想 / 編程模型——觀察者模式。

觀察者模式概述

觀察者模式(Observer Pattern)是一種行為設計模式,其核心在于建立對象間的動態(tài)訂閱-通知機制。

它定義了對象之間的一對多依賴關系,當一個對象(被觀察對象,也稱為主題)的狀態(tài)發(fā)生變化時,所有依賴它的對象(觀察者)都會收到通知并自動更新。

嵌入式系統(tǒng)中,觀察者模式廣泛應用于解耦事件發(fā)布者與訂閱者,特別適合的應用場景:

    處理傳感器數(shù)據(jù)更新硬件狀態(tài)變化多模塊協(xié)作

嵌入式應用場景

1、傳感器數(shù)據(jù)分發(fā)

多個模塊(如顯示、存儲、報警)需要實時獲取傳感器數(shù)據(jù)變化,觀察者模式可將傳感器作為主題(Subject),各模塊作為觀察者(Observer),實現(xiàn)數(shù)據(jù)更新時的自動通知。

類圖:

主題類(SensorSubject)

包含觀察者列表(observers數(shù)組)

維護當前傳感器值(sensor_value)

提供attach和set_value兩個關鍵方法

觀察者接口(ObserverCallback)

    定義統(tǒng)一的update接口對應代碼中的函數(shù)指針類型

具體觀察者類

    DisplayObserver:處理顯示更新LoggerObserver:處理日志記錄AlarmObserver:處理閾值報警
代碼:
#include<stdio.h>

#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;
? ??
? ??// 遍歷所有觀察者進行通知
? ??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: %dn", value);
}

// 觀察者2:日志模塊回
voidlogger_update(int?value)
{
? ??printf("[Logger] Value: %dn", value);
}

// 觀察者3:報警模塊
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;
}

這個例子允許對象(顯示、日志、報警模塊)訂閱另一個對象(傳感器),當主題狀態(tài)變化時自動通知所有觀察者。

注意:這個例子只是為了解釋觀察者模式的基本思想,在單線程環(huán)境下基本實現(xiàn)了觀察者模式的核心功能。若需要模仿應用于實際應用,需要增加線程安全機制、動態(tài)內(nèi)存管理、更完善的錯誤處理等。

2、Zephyr傳感器子系統(tǒng)

在 Zephyr 中,傳感器子系統(tǒng)使用了類似觀察者模式的機制。傳感器驅(qū)動作為主題,當傳感器數(shù)據(jù)更新時,會觸發(fā)相應的事件。

應用程序可以注冊為觀察者,監(jiān)聽這些事件并在數(shù)據(jù)更新時進行處理。

#include<zephyr.h>
#include<device.h>
#include<devicetree.h>
#include<drivers/sensor.h>

// 傳感器事件處理函數(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.%06dn", 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),相當于注冊觀察者
? ??if?(sensor_trigger_set(dev, &trig, sensor_callback) <?0) {
? ? ? ??return;
? ? }

? ??while?(1) {
? ? ? ? k_sleep(K_MSEC(100));
? ? }
}

 

往期相關文章:Zephyr 會成為物聯(lián)網(wǎng)時代RTOS的佼佼者?

3、任務間通信和同步機制

在 RTOS 中,任務之間的通信和同步機制可以類比為觀察者模式。

EventGroupHandle_t xEventGroup;

// 創(chuàng)建事件組
xEventGroup = xEventGroupCreate();

// 任務 1 作為主題設置事件
voidvTask1(?void?*pvParameters )
{
? ??while(1)
? ? {
? ? ? ??// 設置事件位
? ? ? ? xEventGroupSetBits( xEventGroup,?0x01?);
? ? ? ? vTaskDelay( pdMS_TO_TICKS(?1000?) );
? ? }
}

// 任務 2 作為觀察者等待事件
voidvTask2(?void?*pvParameters )
{
? ? EventBits_t uxBits;
? ??while(1)
? ? {
? ? ? ??// 等待事件位
? ? ? ? uxBits = xEventGroupWaitBits(
? ? ? ? ? ? xEventGroup, ??// 事件組句柄
? ? ? ? ? ??0x01, ? ? ? ? ?// 等待的事件位
? ? ? ? ? ? pdTRUE, ? ? ? ?// 退出時清除事件位
? ? ? ? ? ? pdFALSE, ? ? ??// 不需要所有位都設置
? ? ? ? ? ? portMAX_DELAY ?// 無限期等待
? ? ? ? );
? ? ? ??// 處理事件
? ? ? ??if( ( uxBits &?0x01?) !=?0?)
? ? ? ? {
? ? ? ? ? ??// 執(zhí)行相應操作
? ? ? ? }
? ? }
}

事件組(Event Group)就可以看作是一個主題,而等待這些事件的任務則可以看作是觀察者。

當事件組中的某個事件被設置(狀態(tài)改變)時,等待該事件的任務會被喚醒并執(zhí)行相應的操作,就如同觀察者接收到主題的通知后進行更新一樣。

 

往期相關文章:嵌入式事件標志組

4、MQTT

MQTT 是一種輕量級的消息傳輸協(xié)議,主要用于物聯(lián)網(wǎng)設備之間的通信,其在設計和使用上應用了觀察者模式的思想。

其核心概念包括:

發(fā)布者(Publisher):產(chǎn)生消息并將其發(fā)布到特定的主題(Topic)。

主題(Topic):消息的分類標簽,用于區(qū)分不同類型的消息。

代理(Broker):負責接收發(fā)布者的消息,并將消息轉(zhuǎn)發(fā)給訂閱了相應主題的訂閱者。

訂閱者(Subscriber):訂閱一個或多個主題,當這些主題有新消息發(fā)布時,會收到代理轉(zhuǎn)發(fā)的消息。

以上就是本次的分享,如果覺得文章有幫助,麻煩幫忙轉(zhuǎn)發(fā),謝謝!

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

本公眾號專注于嵌入式技術,包括但不限于C/C++、嵌入式、物聯(lián)網(wǎng)、Linux等編程學習筆記,同時,公眾號內(nèi)包含大量的學習資源。歡迎關注,一同交流學習,共同進步!