回调函数与观察者模式

https://mp.weixin.qq.com/s?__biz=MzIyODQ4MDI0NA==&mid=2247492530&idx=1&sn=826820b2953e6861438e45f0afd51054&chksm=e9cd5836af60b2eea9a8dbcbe34f5b2529537df7f21a4cdf472687199546201e8fbe2820de4d&mpshare=1&scene=24&srcid=0221ppWQolNR35VLSoGbl1Ms&sharer_shareinfo=5db7ab5987beb6404db3aac6e36bf822&sharer_shareinfo_first=5db7ab5987beb6404db3aac6e36bf822#rd以下内容全部来自以上链接,本文只做搬运(个人笔记)

1、为什么会有耦合问题出现?

开始先讲个例子:关于按键驱动。

我们在按键驱动 里面,往往同样会写,在按键被按下后,执行相应动作 ,恰恰这个动作又与按键本身无关,这就造成了耦合,即按键驱动里面,有其他的文件的函数,驱动它本应该只关心只与按键有关的事情。(疑问:那能不能在外面读取、获得按键的值后,再执行对应的操作,不一定都要放驱动里面?可能放在驱动里面的好处是,测试代码也可以在驱动文件内完成)

2、回调函数解耦操作

以上驱动代码里面只有与按键相关的代码,即没有包含别的头文件

应用层,注册回调函数:有个精华地方,注册函数可以回参数

button_register_callback(BUTTON_EVENT_SINGLE_CLICK, on_button_single_click, NULL);

button_register_callback(BUTTON_EVENT_LONG_PRESS, on_button_long_press, &led_for_long_press);

3、观察者登场

先不看subject的那些函数,因为看不懂。主要看subject_t结构体与observer_t变量:

subject_t的:num_observers有几个需要接收消息的对象;

subject_set_value会通知所有订阅了g_temp_sensor的设备

以上代码补全后,如下

cpp 复制代码
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

// -------------------------- observer.h 内容 --------------------------
typedef struct {
    // update 方法,当被观察者状态改变时调用
    // observer 指针用于区分不同的观察者实例
    void (*update)(void *observer, int value);
} observer_t;

// -------------------------- subject.h 内容 --------------------------
#define MAX_OBSERVERS 10
typedef struct {
    observer_t* observers[MAX_OBSERVERS];
    int num_observers;
    int value; // 被观察的状态(温度)
} subject_t;

void subject_init(subject_t *sub);
bool subject_attach(subject_t *sub, observer_t *obs);
bool subject_detach(subject_t *sub, observer_t *obs);
void subject_notify(subject_t *sub);
void subject_set_value(subject_t *sub, int new_value);

// -------------------------- subject.c 内容 --------------------------
// 初始化被观察者
void subject_init(subject_t *sub) {
    if (sub == NULL) return; // 空指针保护
    sub->num_observers = 0;  // 观察者数量初始化为0
    sub->value = 0;          // 初始温度设为0
    // 清空观察者列表(避免野指针)
    memset(sub->observers, 0, sizeof(sub->observers));
}

// 添加观察者(返回是否成功)
bool subject_attach(subject_t *sub, observer_t *obs) {
    // 边界检查:被观察者/观察者为空、列表已满
    if (sub == NULL || obs == NULL || sub->num_observers >= MAX_OBSERVERS) {
        return false;
    }
    // 避免重复添加同一个观察者
    for (int i = 0; i < sub->num_observers; ++i) {
        if (sub->observers[i] == obs) {
            return false;
        }
    }
    // 添加到列表末尾,数量+1
    sub->observers[sub->num_observers++] = obs;
    return true;
}

// 移除观察者(返回是否成功)
bool subject_detach(subject_t *sub, observer_t *obs) {
    if (sub == NULL || obs == NULL || sub->num_observers == 0) {
        return false;
    }
    // 查找观察者位置
    int target_idx = -1;
    for (int i = 0; i < sub->num_observers; ++i) {
        if (sub->observers[i] == obs) {
            target_idx = i;
            break;
        }
    }
    // 未找到则返回失败
    if (target_idx == -1) {
        return false;
    }
    // 找到则移动数组(覆盖被移除的位置)
    for (int i = target_idx; i < sub->num_observers - 1; ++i) {
        sub->observers[i] = sub->observers[i + 1];
    }
    sub->num_observers--;          // 数量-1
    sub->observers[sub->num_observers] = NULL; // 清空最后一个位置(可选,安全)
    return true;
}

void subject_notify(subject_t *sub) {
    if (sub == NULL) return; // 空指针保护
    for (int i = 0; i < sub->num_observers; ++i) {
        // 调用每个观察者的 update 方法
        if (sub->observers[i] != NULL && sub->observers[i]->update != NULL) {
            sub->observers[i]->update(sub->observers[i], sub->value);
        }
    }
}

void subject_set_value(subject_t *sub, int new_value) {
    if (sub == NULL) return; // 空指针保护
    if (sub->value != new_value) {
        sub->value = new_value;
        // 状态改变,通知所有观察者
        subject_notify(sub);
    }
}

// -------------------------- display_module.c 内容 --------------------------
void display_update(void *observer, int temperature) {
    printf("Display: Temperature is now %d C\n", temperature);
}

observer_t g_display_observer = { .update = display_update };

// -------------------------- alarm_module.c 内容 --------------------------
void alarm_update(void *observer, int temperature) {
    if (temperature > 50) {
        printf("Alarm: DANGER! Temperature is %d C!\n", temperature);
    }
}

observer_t g_alarm_observer = { .update = alarm_update };

// -------------------------- main.c 内容 --------------------------
subject_t g_temp_sensor;

int main() {
    // 初始化温度传感器
    subject_init(&g_temp_sensor);

    // 订阅观察者
    subject_attach(&g_temp_sensor, &g_display_observer);
    subject_attach(&g_temp_sensor, &g_alarm_observer);

    // 模拟温度变化
    subject_set_value(&g_temp_sensor, 25); // 仅显示器输出:25℃
    subject_set_value(&g_temp_sensor, 60); // 显示器+报警器输出:60℃ + 危险提示

    // 取消报警器订阅
    subject_detach(&g_temp_sensor, &g_alarm_observer);
    subject_set_value(&g_temp_sensor, 70); // 仅显示器输出:70℃

    return 0;
}
相关推荐
资深web全栈开发2 天前
设计模式之观察者模式 (Observer Pattern)
观察者模式·设计模式
知无不研9 天前
c++的设计模式(常用)
c++·观察者模式·单例模式·设计模式·简单工厂模式
金宗汉11 天前
《宇宙递归拓扑学:基于动态范畴与拓扑熵的跨尺度统一场理论》
人工智能·观察者模式·访问者模式·命令模式
短剑重铸之日19 天前
《设计模式》第四篇:观察者模式
java·后端·观察者模式·设计模式
「QT(C++)开发工程师」21 天前
C++ 观察者模式
java·c++·观察者模式
琹箐22 天前
设计模式——观察者模式
观察者模式·设计模式
进击的小头24 天前
行为型模式:观察者模式
c语言·观察者模式
不穿格子的程序员1 个月前
设计模式篇2——观察者模式:以直播间送礼系统举例
java·观察者模式·设计模式
Engineer邓祥浩1 个月前
设计模式学习(19) 23-17 观察者模式
学习·观察者模式·设计模式