C语言实现观察者模式
观察者模式(Observer Pattern)的核心是建立对象间的一对多依赖关系:当一个对象(主题)的状态发生变化时,所有依赖它的对象(观察者)会自动收到通知并更新。在C语言中,可以通过主题维护观察者列表+观察者实现统一更新接口实现:主题存储观察者指针,状态变化时遍历列表调用观察者的更新函数。
C语言实现观察者模式的思路
- 抽象观察者(Observer):定义观察者的统一更新接口(函数指针),用于接收主题通知。
 - 具体观察者(Concrete Observer):实现更新接口,根据主题状态变化执行具体操作。
 - 抽象主题(Subject):定义主题的接口(添加观察者、移除观察者、通知所有观察者),并维护观察者列表。
 - 具体主题(Concrete Subject):存储状态,状态变化时调用通知方法。
 
示例:气象站(主题)与多个显示器(观察者)
气象站(主题)会定期更新温度、湿度数据,多个显示器(如控制台显示器、图形显示器)作为观察者,实时显示最新数据。
步骤1:定义抽象观察者接口
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 前向声明主题,避免循环依赖
typedef struct Subject Subject;// 抽象观察者:定义更新接口(接收主题的最新状态)
typedef struct Observer {// 更新方法:参数为主题(可主动获取状态)或直接传入状态void (*update)(struct Observer* self, Subject* subject);// 销毁观察者void (*destroy)(struct Observer* self);
} Observer;
步骤2:定义抽象主题接口
主题需维护观察者列表,并提供添加、移除、通知观察者的方法。
// 观察者节点:用于链表存储观察者
typedef struct ObserverNode {Observer* observer;struct ObserverNode* next;
} ObserverNode;// 抽象主题:气象站(维护观察者列表)
typedef struct Subject {float temperature; // 温度(状态)float humidity;    // 湿度(状态)ObserverNode* observers; // 观察者链表// 主题接口void (*attach)(struct Subject* self, Observer* observer); // 添加观察者void (*detach)(struct Subject* self, Observer* observer); // 移除观察者void (*notify)(struct Subject* self); // 通知所有观察者// 获取状态(供观察者调用)void (*get_state)(struct Subject* self, float* temp, float* humi);
} Subject;
步骤3:实现具体主题(气象站)
具体主题实现主题接口,状态变化时触发通知。
// 具体主题:气象站
typedef struct {Subject subject; // 继承抽象主题
} WeatherStation;// 添加观察者(链表头插)
static void weather_attach(Subject* self, Observer* observer) {if (!observer) return;ObserverNode* node = (ObserverNode*)malloc(sizeof(ObserverNode));if (!node) return;node->observer = observer;node->next = self->observers; // 插入链表头部self->observers = node;printf("添加观察者成功\n");
}// 移除观察者(从链表中删除)
static void weather_detach(Subject* self, Observer* observer) {if (!observer || !self->observers) return;ObserverNode* prev = NULL;ObserverNode* curr = self->observers;while (curr) {if (curr->observer == observer) {if (prev) {prev->next = curr->next; // 前节点指向后节点} else {self->observers = curr->next; // 更新头节点}free(curr); // 释放节点printf("移除观察者成功\n");return;}prev = curr;curr = curr->next;}
}// 通知所有观察者(遍历链表调用update)
static void weather_notify(Subject* self) {ObserverNode* curr = self->observers;while (curr) {curr->observer->update(curr->observer, self); // 调用观察者的更新方法curr = curr->next;}
}// 提供状态获取接口(观察者通过此方法获取最新数据)
static void weather_get_state(Subject* self, float* temp, float* humi) {*temp = self->temperature;*humi = self->humidity;
}// 气象站状态更新(模拟传感器数据变化)
void weather_station_update(WeatherStation* station, float temp, float humi) {station->subject.temperature = temp;station->subject.humidity = humi;printf("\n气象站数据更新:温度=%.1f℃,湿度=%.1f%%\n", temp, humi);station->subject.notify((Subject*)station); // 状态变化,通知所有观察者
}// 创建气象站(初始化主题)
WeatherStation* weather_station_create() {WeatherStation* station = (WeatherStation*)malloc(sizeof(WeatherStation));if (!station) return NULL;// 初始化主题状态station->subject.temperature = 0;station->subject.humidity = 0;station->subject.observers = NULL;// 绑定主题接口方法station->subject.attach = weather_attach;station->subject.detach = weather_detach;station->subject.notify = weather_notify;station->subject.get_state = weather_get_state;return station;
}// 销毁气象站(释放观察者链表)
void weather_station_destroy(WeatherStation* station) {if (!station) return;ObserverNode* curr = station->subject.observers;while (curr) {ObserverNode* temp = curr;curr = curr->next;temp->observer->destroy(temp->observer); // 销毁观察者free(temp); // 销毁节点}free(station);
}
步骤4:实现具体观察者(显示器)
多个观察者实现update方法,接收主题通知并更新显示。
4.1 控制台显示器
// 具体观察者1:控制台显示器
typedef struct {Observer observer; // 继承观察者接口char name[32];     // 显示器名称
} ConsoleDisplay;// 控制台显示器的更新方法:获取主题状态并打印
static void console_update(Observer* self, Subject* subject) {ConsoleDisplay* display = (ConsoleDisplay*)self;float temp, humi;subject->get_state(subject, &temp, &humi); // 从主题获取最新状态printf("[%s] 显示:温度=%.1f℃,湿度=%.1f%%\n", display->name, temp, humi);
}// 控制台显示器的销毁
static void console_destroy(Observer* self) {free(self);
}// 创建控制台显示器
Observer* console_display_create(const char* name) {ConsoleDisplay* display = (ConsoleDisplay*)malloc(sizeof(ConsoleDisplay));if (!display) return NULL;// 绑定观察者接口display->observer.update = console_update;display->observer.destroy = console_destroy;strncpy(display->name, name, sizeof(display->name)-1);return (Observer*)display;
}
4.2 图形显示器(模拟)
// 具体观察者2:图形显示器(简化实现)
typedef struct {Observer observer;char name[32];
} GraphicDisplay;// 图形显示器的更新方法
static void graphic_update(Observer* self, Subject* subject) {GraphicDisplay* display = (GraphicDisplay*)self;float temp, humi;subject->get_state(subject, &temp, &humi);printf("[%s] 绘制图表:温度=%.1f℃,湿度=%.1f%%\n", display->name, temp, humi);
}// 图形显示器的销毁
static void graphic_destroy(Observer* self) {free(self);
}// 创建图形显示器
Observer* graphic_display_create(const char* name) {GraphicDisplay* display = (GraphicDisplay*)malloc(sizeof(GraphicDisplay));if (!display) return NULL;display->observer.update = graphic_update;display->observer.destroy = graphic_destroy;strncpy(display->name, name, sizeof(display->name)-1);return (Observer*)display;
}
步骤5:使用观察者模式
主题(气象站)添加观察者(显示器),状态变化时自动通知所有观察者。
int main() {// 1. 创建主题(气象站)WeatherStation* station = weather_station_create();if (!station) return 1;// 2. 创建观察者(显示器)Observer* console1 = console_display_create("客厅控制台");Observer* graphic1 = graphic_display_create("卧室图形屏");// 3. 主题添加观察者station->subject.attach((Subject*)station, console1);station->subject.attach((Subject*)station, graphic1);// 4. 模拟气象站数据更新(触发通知)weather_station_update(station, 25.5f, 60.0f);weather_station_update(station, 26.0f, 58.5f);// 5. 移除一个观察者station->subject.detach((Subject*)station, console1);printf("\n移除控制台后的数据更新:\n");weather_station_update(station, 24.8f, 62.0f);// 6. 清理资源weather_station_destroy(station);return 0;
}
输出结果
添加观察者成功
添加观察者成功气象站数据更新:温度=25.5℃,湿度=60.0%
[客厅控制台] 显示:温度=25.5℃,湿度=60.0%
[卧室图形屏] 绘制图表:温度=25.5℃,湿度=60.0%气象站数据更新:温度=26.0℃,湿度=58.5%
[客厅控制台] 显示:温度=26.0℃,湿度=58.5%
[卧室图形屏] 绘制图表:温度=26.0℃,湿度=58.5%移除观察者成功移除控制台后的数据更新:
气象站数据更新:温度=24.8℃,湿度=62.0%
[卧室图形屏] 绘制图表:温度=24.8℃,湿度=62.0%
核心思想总结
- 松耦合:主题与观察者通过接口交互,主题无需知道观察者的具体类型,新增/移除观察者无需修改主题代码(符合开放-封闭原则)。
 - 自动通知:主题状态变化时,所有注册的观察者会被自动通知,无需手动调用更新。
 - 一对多依赖:一个主题可对应多个观察者(如气象站同时连接多个显示器),观察者之间互不影响。
 
C语言通过链表维护观察者列表,结合函数指针实现更新接口,完美模拟了观察者模式的核心,适合实现事件监听、状态同步等场景(如GUI事件、传感器数据分发、消息订阅系统)。
