回调函数与观察者模式

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;
}
相关推荐
sg_knight19 小时前
设计模式实战:观察者模式(Observer)
python·观察者模式·设计模式
大数据新鸟3 天前
设计模式详解——观察者模式
观察者模式·设计模式
无籽西瓜a6 天前
【西瓜带你学设计模式 | 第二期-观察者模式】观察者模式——推模型与拉模型实现、优缺点与适用场景
java·后端·观察者模式·设计模式
君主黑暗7 天前
设计模式-观察者模式
观察者模式·设计模式
砍光二叉树7 天前
【设计模式】行为型-观察者模式
java·观察者模式·设计模式
Aaron_dw17 天前
QT软件开发设计模式-观察者模式
qt·观察者模式·设计模式
Allen_LVyingbo18 天前
PostgreSQL动态分区裁剪技术:查询性能优化解析(2026年版)
数据库·算法·观察者模式·postgresql·性能优化·架构
蜜獾云20 天前
设计模式之观察者模式:监听目标对象的状态改变
观察者模式·设计模式·rxjava
逆境不可逃21 天前
【从零入门23种设计模式19】行为型之观察者模式
java·开发语言·算法·观察者模式·leetcode·设计模式·动态规划
JTCC23 天前
Java 设计模式西游篇 - 第五回:装饰者模式添法力 悟空披挂新战袍
java·观察者模式·设计模式