【设计模式】观察者模式 参考文章一文讲清楚观察者模式代码-图#includestdio.h// 前置声明typedefstruct_subject_tsubject_t;typedefstruct_observer_tobserver_t;voidsubject_attach(subject_t*sub,observer_t*obs);voidsubject_detach(subject_t*sub,observer_t*obs);typedefstruct_observer_t{void(*update)(struct_observer_t*,int,int);// 修正函数指针语法struct_observer_t*next;struct_subject_t*subject;// 添加前向声明}observer_t;voidobserver_init(observer_t*obs,subject_t*subject){obs-updateNULL;obs-nextNULL;obs-subjectsubject;subject_attach(subject,obs);}voidobserver_deinit(observer_t*obs)// 移除多余参数{subject_detach(obs-subject,obs);}voidobserver_update(observer_t*obs,inttemp,inthum){if(obs-update!NULL){obs-update(obs,temp,hum);}}// subject_t 结构定义示例struct_subject_t{observer_t*obs;};// 静态初始化主题对象voidsubject_init(subject_t*subject){subject-obsNULL;}// 需要实现的观察者注册函数voidsubject_attach(subject_t*subject,observer_t*obs){if(!subject-obs){subject-obsobs;}else{observer_t*cursubject-obs;while(cur-next)curcur-next;cur-nextobs;}}// 需要实现的观察者注销函数voidsubject_detach(subject_t*subject,observer_t*obs){observer_t**indirectsubject-obs;// 使用二级指针简化操作while(*indirect){if(*indirectobs){*indirectobs-next;// 绕过目标节点obs-nextNULL;// 重置观察者的next指针return;}indirect(*indirect)-next;}}// 数据通知订阅者voidsubject_notify(subject_t*subject,inttemp,inthum){observer_t*_obssubject-obs;while(_obs){observer_update(_obs,temp,hum);_obs_obs-next;}}typedefstruct_display_t{observer_tobs;}display_t;voiddisplay_show(observer_t*obs,inttemp,inthum);// 静态构造函数voiddisplay_init(display_t*display,subject_t*subject){observer_init(display-obs,subject);display-obs.updatedisplay_show;}// 模拟屏幕显示voiddisplay_show(observer_t*obs,inttemp,inthum){printf(temp:%d\n,temp);printf(hum:%d\n,hum);}typedefstruct_ble_t{observer_tobs;}ble_t;voidble_send(observer_t*obs,inttemp,inthum);// 静态构造函数修正变量名错误voidble_init(ble_t*ble,subject_t*subject){observer_init(ble-obs,subject);// 修正 display- → ble-ble-obs.updateble_send;}// 模拟蓝牙发送voidble_send(observer_t*obs,inttemp,inthum){printf(send temp:%d\n,temp);printf(send hum:%d\n,hum);}typedefstruct_sensor_t{subject_tsubject;inttemp;inthum;}sensor_t;// 静态构造函数voidsensor_init(sensor_t*sensor){sensor-temp0;sensor-hum0;subject_init(sensor-subject);}// 模拟温湿度传感器读取数值voidsensor_get(sensor_t*sensor,inttemp,inthum){sensor-temptemp;sensor-humhum;subject_notify(sensor-subject,temp,hum);// 更安全的指针转换}intmain(){// 创建传感器实例sensor_tmy_sensor;sensor_init(my_sensor);// 创建显示设备和蓝牙设备display_tscreen;display_init(screen,(subject_t*)my_sensor);ble_tble_device;ble_init(ble_device,(subject_t*)my_sensor);// 第一次数据更新两个设备都应响应printf( First Update \n);sensor_get(my_sensor,25,60);// 移除蓝牙设备通过反初始化printf(\n Detaching BLE \n);observer_deinit(ble_device.obs);// 第二次数据更新仅显示设备响应printf(\n Second Update \n);sensor_get(my_sensor,26,58);return0;}想法观察者模式是一种消息从下往上的方式类似与回调函数当下面发现了数据变化通知上面。应用层模块的输出通常是上层一步步往下最终底层实现但是底层的数据想要往上传1种通过上层的查询【数据拉模型】2种是通过“通知/回调”的形式上传【数据推模型】回调函数【消费者】上层提供具体回调方法和数据入口【生产者】下层提供抽象回调方法配置attach。典型的就是串口数据接收观察者模式观察者模式利用面向对象的方式讲回调函数接口直接封装起来使用其中“观察者”通常是应用中上层的【消费者】而“被观察对象”通常是下层【生成者】。被观察者通过notify方法将数据上传到观察者端这个过程类似“回调函数”的作用下层应用通知触发上层应用下层被观察者仅通过抽象接口如Observer.update()通知上层不依赖具体实现观察者模式的notify可通知多个观察者一对多而回调通常是一对一的关系观察者模式通过中介如Subject类完全解耦双方回调中调用方需持有被调用方的引用如Callback接口实例观察者模式对比订阅发布模式维度观察者模式 (Observer Pattern)发布-订阅模式 (Pub-Sub Pattern)耦合度松耦合观察者直接依赖被观察者被观察者维护观察者列表完全解耦通过中介传递消息发布者和订阅者互不知晓依赖中介通信方式同步调用立即触发回调的方式立刻执行异步分发通过消息队列/事件总线先登记后处理核心角色被观察者(Subject) 观察者(Observer)发布者(Publisher) 订阅者(Subscriber) 中介(Broker)交互方式被观察者直接调用观察者的方法发布者和订阅者通过主题(Topic)间接通信性能影响可能阻塞观察者处理耗时高吞吐支持削峰填谷典型应用场景GUI事件、单应用组件联动微服务通信、分布式系统事件通知代码实现复杂度简单需手动维护观察者列表较复杂需引入中介系统经典案例JavaObservable/ObserverRedis PUB/SUB、Kafka、RabbitMQ扩展性低新增观察者需修改被观察者高动态增减订阅者无需修改发布者适用规模小型系统或模块内部适合对象间有明确依赖关系、需要实时同步响应的场景如UI更新、状态同步中大型系统或跨服务通信适合需要解耦、异步处理或跨系统通信的场景如微服务、分布式日志依赖倒置原则Dependency Inversion Principle, DIP其核心思想是通过抽象解耦高层模块与低层模块的依赖关系提升系统的灵活性、可维护性和扩展性。依赖倒置原则的定义高层模块不应依赖低层模块两者都应依赖于抽象接口或抽象类。抽象不应依赖细节抽象层定义规范具体实现细节应依赖抽象核心目标通过面向接口编程减少模块间的直接耦合使系统更适应变化。为什么需要依赖倒置传统设计的缺陷高层模块直接调用低层模块导致低层改动时高层必须同步修改如更换数据库需重写业务逻辑代码复用性差并行开发困难如团队需等待底层模块完成才能开发高层依赖倒置的优势解耦高层模块通过抽象接口调用低层功能低层实现可自由替换扩展性新增功能只需实现接口无需修改现有代码符合开闭原则测试友好可通过Mock对象模拟接口独立测试高层逻辑

本月热点