C语言实现观察者模式

观察者模式(Observer Pattern)的核心是建立对象间的一对多依赖关系 :当一个对象(主题)的状态发生变化时,所有依赖它的对象(观察者)会自动收到通知并更新。在C语言中,可以通过主题维护观察者列表+观察者实现统一更新接口实现:主题存储观察者指针,状态变化时遍历列表调用观察者的更新函数。

C语言实现观察者模式的思路

  1. 抽象观察者(Observer):定义观察者的统一更新接口(函数指针),用于接收主题通知。
  2. 具体观察者(Concrete Observer):实现更新接口,根据主题状态变化执行具体操作。
  3. 抽象主题(Subject):定义主题的接口(添加观察者、移除观察者、通知所有观察者),并维护观察者列表。
  4. 具体主题(Concrete Subject):存储状态,状态变化时调用通知方法。

示例:气象站(主题)与多个显示器(观察者)

气象站(主题)会定期更新温度、湿度数据,多个显示器(如控制台显示器、图形显示器)作为观察者,实时显示最新数据。

步骤1:定义抽象观察者接口
c 复制代码
#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:定义抽象主题接口

主题需维护观察者列表,并提供添加、移除、通知观察者的方法。

c 复制代码
// 观察者节点:用于链表存储观察者
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:实现具体主题(气象站)

具体主题实现主题接口,状态变化时触发通知。

c 复制代码
// 具体主题:气象站
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 控制台显示器
c 复制代码
// 具体观察者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 图形显示器(模拟)
c 复制代码
// 具体观察者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:使用观察者模式

主题(气象站)添加观察者(显示器),状态变化时自动通知所有观察者。

c 复制代码
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%

核心思想总结

  1. 松耦合:主题与观察者通过接口交互,主题无需知道观察者的具体类型,新增/移除观察者无需修改主题代码(符合开放-封闭原则)。
  2. 自动通知:主题状态变化时,所有注册的观察者会被自动通知,无需手动调用更新。
  3. 一对多依赖:一个主题可对应多个观察者(如气象站同时连接多个显示器),观察者之间互不影响。

C语言通过链表维护观察者列表,结合函数指针实现更新接口,完美模拟了观察者模式的核心,适合实现事件监听、状态同步等场景(如GUI事件、传感器数据分发、消息订阅系统)。

相关推荐
代码雕刻家3 小时前
1.4.课设实验-数据结构-单链表-文教文化用品品牌2.0
c语言·数据结构
侯小啾3 小时前
【22】C语言 - 二维数组详解
c语言·数据结构·算法
qq_479875434 小时前
Linux time function in C/C++【2】
linux·c语言·c++
yuuki2332334 小时前
【数据结构】双向链表的实现
c语言·数据结构·后端
ol木子李lo5 小时前
Doxygen入门指南:从注释到自动文档
c语言·c++·windows·编辑器·visual studio code·visual studio·doxygen
代码雕刻家5 小时前
1.6.课设实验-数据结构-栈、队列-银行叫号系统2.0
c语言·数据结构
yq14682860905 小时前
C (统计二进制中“1“的个数)
c语言·开发语言·算法
是苏浙6 小时前
零基础入门C语言之数据在内存中的存储
c语言·开发语言
Shylock_Mister6 小时前
ESP32事件组替代全局变量:高效控制任务循环
c语言·单片机·物联网