🎯 行为型设计模式详解
行为型模式关注对象之间的通信、职责划分,以及算法的封装。本文详细介绍观察者模式,并补充策略模式、模板方法模式、责任链模式等常用行为型模式。
一、观察者模式
1.1 定义与应用场景
核心思想:如果一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的改变。
观察者模式(Observer Pattern)定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
角色说明:
| 角色 | 说明 |
|---|---|
| Subject(主题/被观察者) | 维护观察者列表,状态变化时通知所有观察者 |
| Observer(观察者) | 定义更新接口,收到通知时执行相应操作 |
| ConcreteSubject(具体主题) | 存储具体状态,状态改变时触发通知 |
| ConcreteObserver(具体观察者) | 实现更新接口,定义收到通知后的具体行为 |
1.2 实际案例
📱 微信公众号案例
微信公众号是观察者模式的典型应用:
- 公众号 = 被观察者(Subject)
- 订阅用户 = 观察者(Observer)
- 用户订阅公众号后,公众号发布新消息时,所有订阅用户都能收到通知
cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 观察者接口
class Observer {
public:
virtual void update(const string& message) = 0;
virtual ~Observer() = default;
};
// 被观察者接口
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify(const string& message) = 0;
virtual ~Subject() = default;
};
// 具体被观察者:微信公众号
class WeChatOfficialAccount : public Subject {
private:
vector<Observer*> observers;
string accountName;
public:
WeChatOfficialAccount(const string& name) : accountName(name) {}
void attach(Observer* observer) override {
observers.push_back(observer);
cout << "新用户订阅了公众号: " << accountName << endl;
}
void detach(Observer* observer) override {
for (auto it = observers.begin(); it != observers.end(); ++it) {
if (*it == observer) {
observers.erase(it);
cout << "用户取消订阅公众号: " << accountName << endl;
break;
}
}
}
void notify(const string& message) override {
cout << "\n【" << accountName << "发布新消息】: " << message << endl;
for (auto observer : observers) {
observer->update(message);
}
}
};
// 具体观察者:微信用户
class WeChatUser : public Observer {
private:
string userName;
public:
WeChatUser(const string& name) : userName(name) {}
void update(const string& message) override {
cout << " 用户 [" << userName << "] 收到消息: " << message << endl;
}
};
// 使用示例
int main() {
// 创建公众号
WeChatOfficialAccount techBlog("技术周刊");
// 创建用户
WeChatUser alice("Alice");
WeChatUser bob("Bob");
WeChatUser charlie("Charlie");
// 用户订阅公众号
techBlog.attach(&alice);
techBlog.attach(&bob);
techBlog.attach(&charlie);
// 公众号发布消息
techBlog.notify("设计模式详解系列文章上线了!");
// 用户取消订阅
techBlog.detach(&bob);
// 再次发布消息
techBlog.notify("新文章:深入理解观察者模式");
return 0;
}
输出结果:
新用户订阅了公众号: 技术周刊
新用户订阅了公众号: 技术周刊
新用户订阅了公众号: 技术周刊
【技术周刊发布新消息】: 设计模式详解系列文章上线了!
用户 [Alice] 收到消息: 设计模式详解系列文章上线了!
用户 [Bob] 收到消息: 设计模式详解系列文章上线了!
用户 [Charlie] 收到消息: 设计模式详解系列文章上线了!
用户取消订阅公众号: 技术周刊
【技术周刊发布新消息】: 新文章:深入理解观察者模式
用户 [Alice] 收到消息: 新文章:深入理解观察者模式
用户 [Charlie] 收到消息: 新文章:深入理解观察者模式
🌡️ 气象站案例
气象站可以将每天预测到的温度、湿度、气压等以公告的形式发布给各个第三方网站,如果天气数据有更新,要能够实时通知给第三方。
- 气象局 = 被观察者
- 第三方网站 = 观察者
1.3 推送模型 vs 拉取模型(补充)
| 模型 | 说明 | 优缺点 |
|---|---|---|
| 推送模型 | 主题主动将数据传递给观察者 | ✅ 优点:观察者直接获得数据 ❌ 缺点:观察者可能不需要所有数据 |
| 拉取模型 | 主题只通知,观察者主动获取数据 | ✅ 优点:观察者按需获取 ❌ 缺点:需要多次调用 |
推送模型示例:
cpp
void notify(const string& message) {
for (auto observer : observers) {
observer->update(message); // 推送消息
}
}
拉取模型示例:
cpp
void notify() {
for (auto observer : observers) {
observer->update(this); // 传递主题引用,观察者自行拉取数据
}
}
// 观察者拉取数据
void update(Subject* subject) override {
string data = subject->getData(); // 主动拉取
// 处理数据
}