行为型模式:⑦观察者模式(Observer Pattern)

行为型模式:⑦观察者模式(Observer Pattern)

核心思想

定义对象间的 “一对多” 依赖关系,当一个对象(主题 / Subject)的状态发生变化时,所有依赖它的对象(观察者 / Observer)都会自动收到通知并进行更新。核心价值是:
解耦主题与观察者:主题无需知道具体观察者的实现,仅依赖观察者的抽象接口;
动态扩展:可随时添加 / 移除观察者,不影响主题和其他观察者;
统一通知:主题状态变化时,自动同步所有相关观察者,无需手动调用。

核心本质

事件驱动 + 依赖倒置,将 “状态变化” 作为事件,观察者订阅事件,主题发布事件,实现联动更新。

典型场景

消息通知系统(如公众号推送、邮件订阅);
界面组件联动(如滑动条拖动时,进度显示、数值输入框同步更新);
数据监控系统(如传感器数据变化时,仪表盘、报警模块响应);
发布 - 订阅(Pub/Sub)架构(如消息队列的核心模式)。

C 语言实现(结构体 + 函数指针 + 动态观察者列表)

C 语言无类和继承,通过 “结构体模拟角色”“函数指针定义观察者接口”“动态数组维护观察者列表”,实现主题与观察者的解耦。核心是:主题持有观察者列表,观察者提供统一的更新函数,主题状态变化时遍历调用。

场景示例

天气监控系统:
主题(WeatherData):存储温度、湿度、气压,提供数据更新、注册 / 移除观察者、通知观察者接口;
观察者(Observer):提供update函数指针,接收主题的状态更新(如当前天气显示、统计数据显示);
具体观察者:CurrentConditionsDisplay(当前天气显示)、StatisticsDisplay(统计数据显示)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 最大观察者数量(动态数组初始容量,可扩展)
#define INIT_OBSERVER_CAP 4

// 前置声明:主题结构体(供观察者使用)
typedef struct Subject Subject;

// -------------------------- 1. 观察者(Observer):统一更新接口 --------------------------
// 观察者结构体:包含更新函数指针(核心接口)和用户数据(区分不同观察者)
typedef struct Observer {
    // 更新函数:主题状态变化时调用,接收主题指针和新状态
    void (*update)(struct Observer* self, const Subject* subject, float temp, float humidity, float pressure);
    // 销毁观察者
    void (*destroy)(struct Observer* self);
    // 观察者名称(用于区分)
    char name[32];
    // 扩展数据(可选,存储观察者自身状态)
    void* data;
} Observer;

// -------------------------- 2. 主题(Subject):管理观察者+通知逻辑 --------------------------
typedef struct Subject {
    // 动态观察者列表(指针数组)
    Observer** observers;
    int count;   // 当前观察者数量
    int capacity; // 观察者列表容量
    // 主题状态
    float temperature;
    float humidity;
    float pressure;
    // 主题操作接口
    void (*register_observer)(struct Subject* self, Observer* observer); // 注册观察者
    void (*remove_observer)(struct Subject* self, Observer* observer);  // 移除观察者
    void (*notify_observers)(struct Subject* self);                     // 通知所有观察者
    void (*set_measurements)(struct Subject* self, float temp, float humidity, float pressure); // 更新状态并通知
    void (*destroy)(struct Subject* self);
} Subject;

// 主题:扩展观察者列表容量(动态扩容)
static void subject_resize_observers(Subject* self) {
    self->capacity *= 2;
    self->observers = (Observer**)realloc(self->observers, sizeof(Observer*) * self->capacity);
    if (self->observers == NULL) {
        perror("realloc failed");
        exit(EXIT_FAILURE);
    }
}

// 主题:注册观察者
void subject_register_observer(Subject* self, Observer* observer) {
    if (self->count >= self->capacity) {
        subject_resize_observers(self);
    }
    // 避免重复注册
    for (int i = 0; i < self->count; i++) {
        if (self->observers[i] == observer) {
            printf("[主题] 观察者「%s」已注册,无需重复添加\n", observer->name);
            return;
        }
    }
    self->observers[self->count++] = observer;
    printf("[主题] 观察者「%s」注册成功\n", observer->name);
}

// 主题:移除观察者
void subject_remove_observer(Subject* self, Observer* observer) {
    int index = -1;
    for (int i = 0; i < self->count; i++) {
        if (self->observers[i] == observer) {
            index = i;
            break;
        }
    }
    if (index == -1) {
        printf("[主题] 观察者「%s」未注册,无法移除\n", observer->name);
        return;
    }
    // 移除:后续观察者前移
    for (int i = index; i < self->count - 1; i++) {
        self->observers[i] = self->observers[i + 1];
    }
    self->count--;
    printf("[主题] 观察者「%s」移除成功\n", observer->name);
}

// 主题:通知所有观察者(状态变化时调用)
void subject_notify_observers(Subject* self) {
    printf("\n[主题] 状态更新,开始通知所有观察者...\n");
    for (int i = 0; i < self->count; i++) {
        Observer* observer = self->observers[i];
        // 调用观察者的更新函数,传递最新状态
        observer->update(observer, self, self->temperature, self->humidity, self->pressure);
    }
}

// 主题:更新自身状态并通知观察者
void subject_set_measurements(Subject* self, float temp, float humidity, float pressure) {
    self->temperature = temp;
    self->humidity = humidity;
    self->pressure = pressure;
    printf("[主题] 状态更新:温度=%.1f℃,湿度=%.1f%%,气压=%.1f hPa\n", temp, humidity, pressure);
    subject_notify_observers(self);
}

// 主题:销毁(释放观察者列表,观察者由客户端单独销毁)
void subject_destroy(Subject* self) {
    free(self->observers);
    free(self);
    printf("[主题] 已销毁\n");
}

// 创建主题(天气数据)
Subject* create_weather_data() {
    Subject* subject = (Subject*)malloc(sizeof(Subject));
    subject->capacity = INIT_OBSERVER_CAP;
    subject->count = 0;
    subject->observers = (Observer**)malloc(sizeof(Observer*) * subject->capacity);
    if (subject->observers == NULL) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }
    subject->temperature = 0.0f;
    subject->humidity = 0.0f;
    subject->pressure = 0.0f;
    subject->register_observer = subject_register_observer;
    subject->remove_observer = subject_remove_observer;
    subject->notify_observers = subject_notify_observers;
    subject->set_measurements = subject_set_measurements;
    subject->destroy = subject_destroy;
    return subject;
}

// -------------------------- 3. 具体观察者1:当前天气显示(CurrentConditionsDisplay) --------------------------
typedef struct CurrentConditionsDisplay {
    Observer base; // 继承观察者接口(函数指针+名称)
    // 观察者自身状态(可选,缓存最新数据)
    float last_temp;
    float last_humidity;
} CurrentConditionsDisplay;

// 具体观察者:更新函数(实现观察者接口)
void current_display_update(Observer* self, const Subject* subject, float temp, float humidity, float pressure) {
    CurrentConditionsDisplay* display = (CurrentConditionsDisplay*)self;
    display->last_temp = temp;
    display->last_humidity = humidity;
    // 业务逻辑:显示当前天气
    printf("[%s] 收到更新 → 当前天气:温度=%.1f℃,湿度=%.1f%%(气压:%.1f hPa 忽略)\n",
           self->name, temp, humidity, pressure);
}

// 具体观察者:销毁
void current_display_destroy(Observer* self) {
    free(self);
    printf("[%s] 已销毁\n", self->name);
}

// 创建当前天气显示观察者
Observer* create_current_conditions_display() {
    CurrentConditionsDisplay* display = (CurrentConditionsDisplay*)malloc(sizeof(CurrentConditionsDisplay));
    strcpy(display->base.name, "当前天气显示");
    display->base.update = current_display_update;
    display->base.destroy = current_display_destroy;
    display->base.data = display; // 关联自身数据
    display->last_temp = 0.0f;
    display->last_humidity = 0.0f;
    return (Observer*)display;
}

// -------------------------- 4. 具体观察者2:统计数据显示(StatisticsDisplay) --------------------------
typedef struct StatisticsDisplay {
    Observer base;
    // 观察者自身状态:统计数据(平均、最高、最低温度)
    float avg_temp;
    float max_temp;
    float min_temp;
    int update_count; // 更新次数(用于计算平均值)
} StatisticsDisplay;

// 具体观察者:更新函数(实现观察者接口)
void stats_display_update(Observer* self, const Subject* subject, float temp, float humidity, float pressure) {
    StatisticsDisplay* display = (StatisticsDisplay*)self;
    display->update_count++;
    // 统计逻辑:更新平均、最高、最低温度
    display->avg_temp = (display->avg_temp * (display->update_count - 1) + temp) / display->update_count;
    display->max_temp = (temp > display->max_temp) ? temp : display->max_temp;
    display->min_temp = (display->update_count == 1) ? temp : ((temp < display->min_temp) ? temp : display->min_temp);
    // 业务逻辑:显示统计数据
    printf("[%s] 收到更新 → 温度统计:平均=%.1f℃,最高=%.1f℃,最低=%.1f℃(湿度:%.1f%% 忽略)\n",
           self->name, display->avg_temp, display->max_temp, display->min_temp, humidity);
}

// 具体观察者:销毁
void stats_display_destroy(Observer* self) {
    free(self);
    printf("[%s] 已销毁\n", self->name);
}

// 创建统计数据显示观察者
Observer* create_statistics_display() {
    StatisticsDisplay* display = (StatisticsDisplay*)malloc(sizeof(StatisticsDisplay));
    strcpy(display->base.name, "统计数据显示");
    display->base.update = stats_display_update;
    display->base.destroy = stats_display_destroy;
    display->base.data = display;
    display->avg_temp = 0.0f;
    display->max_temp = -100.0f; // 初始值设为极小值
    display->min_temp = 100.0f;  // 初始值设为极大值
    display->update_count = 0;
    return (Observer*)display;
}

// -------------------------- 测试代码(客户端) --------------------------
int main() {
    // 1. 创建主题(天气数据)
    Subject* weather_data = create_weather_data();

    // 2. 创建观察者并注册到主题
    Observer* current_display = create_current_conditions_display();
    Observer* stats_display = create_statistics_display();
    weather_data->register_observer(weather_data, current_display);
    weather_data->register_observer(weather_data, stats_display);

    // 3. 主题状态更新(模拟传感器数据变化)
    printf("\n=== 第一次数据更新 ===\n");
    weather_data->set_measurements(weather_data, 25.5f, 60.0f, 1013.2f);

    printf("\n=== 第二次数据更新 ===\n");
    weather_data->set_measurements(weather_data, 27.3f, 55.0f, 1012.8f);

    printf("\n=== 第三次数据更新(移除统计显示后) ===\n");
    weather_data->remove_observer(weather_data, stats_display);
    weather_data->set_measurements(weather_data, 26.8f, 58.0f, 1013.0f);

    // 4. 销毁资源
    printf("\n=== 销毁所有对象 ===\n");
    current_display->destroy(current_display);
    stats_display->destroy(stats_display);
    weather_data->destroy(weather_data);

    return 0;
}

实现关键要点

1.接口统一(函数指针):Observer结构体通过update函数指针定义统一更新接口,所有具体观察者都实现该接口,主题通知时无需区分观察者类型,实现解耦。
2.动态观察者列表:主题用动态数组存储观察者指针,支持动态扩容和增删,满足灵活扩展需求(如超过初始容量时自动扩容)。
3.主题职责单一:主题仅负责管理观察者(注册 / 移除)和通知,不关心观察者的具体业务逻辑;观察者仅负责接收通知并处理自身业务,不依赖其他观察者。
4.解耦核心:主题与观察者通过 “观察者接口(函数指针)” 关联,主题无需知道观察者的具体实现,新增观察者时仅需实现update函数并注册,无需修改主题代码。
5.内存管理:观察者由客户端创建和销毁,主题仅持有指针,避免循环引用;动态数组需手动扩容和释放,避免内存泄漏。
6.扩展性:新增观察者(如 “气压预警显示”)时,仅需创建新的观察者结构体、实现update函数并注册到主题,主题代码无需修改,符合 “开闭原则”。

C++ 实现(类 + 继承 + 多态 + STL 容器)

C++ 通过 “抽象基类 + 继承” 定义主题和观察者接口,利用多态实现统一通知,STL 容器简化观察者管理,智能指针自动维护内存,代码更符合面向对象风格。

场景示例

同 C 语言:天气监控系统,抽象基类Subject和Observer定义接口,具体主题WeatherData和具体观察者CurrentConditionsDisplay/StatisticsDisplay继承实现。

#include <iostream>
#include <string>
#include <vector>
#include <memory> // 智能指针(自动管理内存)
#include <algorithm> // find_if

using namespace std;

// 前置声明:主题类(供观察者使用)
class Subject;

// -------------------------- 1. 抽象观察者(Observer):基类接口 --------------------------
class Observer {
public:
    virtual ~Observer() = default; // 虚析构:确保子类析构被调用
    // 纯虚函数:更新接口(主题状态变化时调用)
    virtual void update(const Subject& subject, float temp, float humidity, float pressure) = 0;
    // 获取观察者名称(用于区分)
    virtual string getName() const = 0;
};

// -------------------------- 2. 抽象主题(Subject):基类接口 --------------------------
class Subject {
public:
    virtual ~Subject() = default;
    // 纯虚函数:注册观察者
    virtual void registerObserver(shared_ptr<Observer> observer) = 0;
    // 纯虚函数:移除观察者
    virtual void removeObserver(shared_ptr<Observer> observer) = 0;
    // 纯虚函数:通知所有观察者
    virtual void notifyObservers() const = 0;
    // 纯虚函数:更新状态并通知
    virtual void setMeasurements(float temp, float humidity, float pressure) = 0;
};

// -------------------------- 3. 具体主题(WeatherData):实现主题接口 --------------------------
class WeatherData : public Subject {
public:
    WeatherData() : temperature_(0.0f), humidity_(0.0f), pressure_(0.0f) {}

    void registerObserver(shared_ptr<Observer> observer) override {
        // 避免重复注册
        auto it = find_if(observers_.begin(), observers_.end(),
            [&](const shared_ptr<Observer>& obs) { return obs->getName() == observer->getName(); });
        if (it != observers_.end()) {
            cout << "[主题] 观察者「" << observer->getName() << "」已注册,无需重复添加" << endl;
            return;
        }
        observers_.push_back(observer);
        cout << "[主题] 观察者「" << observer->getName() << "」注册成功" << endl;
    }

    void removeObserver(shared_ptr<Observer> observer) override {
        auto it = find_if(observers_.begin(), observers_.end(),
            [&](const shared_ptr<Observer>& obs) { return obs->getName() == observer->getName(); });
        if (it == observers_.end()) {
            cout << "[主题] 观察者「" << observer->getName() << "」未注册,无法移除" << endl;
            return;
        }
        observers_.erase(it);
        cout << "[主题] 观察者「" << observer->getName() << "」移除成功" << endl;
    }

    void notifyObservers() const override {
        cout << "\n[主题] 状态更新,开始通知所有观察者..." << endl;
        for (const auto& observer : observers_) {
            // 多态调用:调用具体观察者的update方法
            observer->update(*this, temperature_, humidity_, pressure_);
        }
    }

    void setMeasurements(float temp, float humidity, float pressure) override {
        temperature_ = temp;
        humidity_ = humidity;
        pressure_ = pressure;
        cout << "[主题] 状态更新:温度=%.1f℃,湿度=%.1f%%,气压=%.1f hPa" << endl;
        notifyObservers(); // 状态更新后自动通知
    }

    // 获取主题状态(供观察者扩展使用,可选)
    float getTemperature() const { return temperature_; }
    float getHumidity() const { return humidity_; }
    float getPressure() const { return pressure_; }

private:
    vector<shared_ptr<Observer>> observers_; // 观察者列表(智能指针自动管理内存)
    float temperature_;  // 温度
    float humidity_;     // 湿度
    float pressure_;     // 气压
};

// -------------------------- 4. 具体观察者1:当前天气显示(CurrentConditionsDisplay) --------------------------
class CurrentConditionsDisplay : public Observer {
public:
    CurrentConditionsDisplay() : last_temp_(0.0f), last_humidity_(0.0f) {}

    void update(const Subject& subject, float temp, float humidity, float pressure) override {
        last_temp_ = temp;
        last_humidity_ = humidity;
        // 业务逻辑:显示当前天气
        cout << "[%s] 收到更新 → 当前天气:温度=%.1f℃,湿度=%.1f%%(气压:%.1f hPa 忽略)" << endl;
    }

    string getName() const override {
        return "当前天气显示";
    }

private:
    float last_temp_;    // 缓存最新温度
    float last_humidity_;// 缓存最新湿度
};

// -------------------------- 5. 具体观察者2:统计数据显示(StatisticsDisplay) --------------------------
class StatisticsDisplay : public Observer {
public:
    StatisticsDisplay() : avg_temp_(0.0f), max_temp_(-100.0f), min_temp_(100.0f), update_count_(0) {}

    void update(const Subject& subject, float temp, float humidity, float pressure) override {
        update_count_++;
        // 统计逻辑:更新平均、最高、最低温度
        avg_temp_ = (avg_temp_ * (update_count_ - 1) + temp) / update_count_;
        max_temp_ = max(temp, max_temp_);
        min_temp_ = (update_count_ == 1) ? temp : min(temp, min_temp_);
        // 业务逻辑:显示统计数据
        cout << "[%s] 收到更新 → 温度统计:平均=%.1f℃,最高=%.1f℃,最低=%.1f℃(湿度:%.1f%% 忽略)" << endl;
    }

    string getName() const override {
        return "统计数据显示";
    }

private:
    float avg_temp_;     // 平均温度
    float max_temp_;     // 最高温度
    float min_temp_;     // 最低温度
    int update_count_;   // 更新次数
};

// -------------------------- 测试代码(客户端) --------------------------
int main() {
    // 1. 创建主题(天气数据)
    shared_ptr<Subject> weather_data = make_shared<WeatherData>();

    // 2. 创建观察者并注册到主题
    shared_ptr<Observer> current_display = make_shared<CurrentConditionsDisplay>();
    shared_ptr<Observer> stats_display = make_shared<StatisticsDisplay>();
    weather_data->registerObserver(current_display);
    weather_data->registerObserver(stats_display);

    // 3. 主题状态更新(模拟传感器数据变化)
    cout << "\n=== 第一次数据更新 ===" << endl;
    weather_data->setMeasurements(25.5f, 60.0f, 1013.2f);

    cout << "\n=== 第二次数据更新 ===" << endl;
    weather_data->setMeasurements(27.3f, 55.0f, 1012.8f);

    cout << "\n=== 第三次数据更新(移除统计显示后) ===" << endl;
    weather_data->removeObserver(stats_display);
    weather_data->setMeasurements(26.8f, 58.0f, 1013.0f);

    return 0; // 智能指针自动销毁所有对象,无需手动释放
}

实现关键要点

1.抽象基类 + 多态:Observer和Subject为抽象基类,纯虚函数定义统一接口,具体类继承并实现,主题通知时通过基类指针 / 引用调用具体观察者的update方法,实现多态分发,解耦主题与具体观察者。
2.智能指针管理内存:用shared_ptr管理观察者,主题的vector存储智能指针,自动处理观察者生命周期,避免野指针和内存泄漏,符合 RAII 原则。
3.STL 容器简化管理:主题用vector存储观察者,find_if+lambda 表达式实现观察者的查找和移除,代码简洁高效,无需手动维护数组索引。
4.封装性更强:主题状态(温度、湿度、气压)为私有成员,通过getter方法供观察者扩展使用,观察者名称通过getName接口返回,避免直接暴露内部数据。
5.扩展性极佳:新增观察者(如 “气压预警显示”)时,仅需继承Observer并实现update和getName方法,注册到主题即可;新增主题时,继承Subject并实现接口,客户端代码无需修改,完全符合 “开闭原则”。
6.类型安全:C++ 的编译期类型检查和多态机制,避免 C 语言中函数指针绑定错误或类型转换风险,代码更可靠。

观察者模式核心总结(C vs C++)

在这里插入图片描述

设计原则

依赖倒置原则:主题依赖观察者抽象,观察者依赖主题抽象,不依赖具体实现;
开闭原则:新增主题或观察者,无需修改现有代码;
单一职责原则:主题管状态管理和通知,观察者管自身业务处理,各司其职。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值