🌟 观察者模式大冒险:一场代码世界的订阅狂欢!🌟
想象一下,你是一个网红博主,每次发布新视频,你的百万粉丝都会立即收到通知!这就是观察者模式的魔力!
🎭 第一章:缘起 - 一个网红博主的烦恼
从前,有个叫"代码侠"的博主,他每次发布新视频都要手动通知每个粉丝:
cpp
// 糟糕的旧时代
void 发布视频() {
制作视频();
打电话通知(粉丝1); // 😫 累死了!
打电话通知(粉丝2);
发邮件通知(粉丝3);
// ... 还有99997个粉丝要通知
}
"代码侠"累得筋疲力尽,直到他遇到了观察者模式这个魔法!
🎯 核心概念:订阅的魔力
cpp
// 新时代的智慧
void 发布视频() {
制作视频();
一键通知所有粉丝(); // 🎉 太轻松了!
}
观察者模式的四大天王:
角色 | 现实比喻 | 职责 |
---|---|---|
Subject(主题) | 网红博主 | 维护粉丝列表,发布内容 |
Observer(观察者) | 粉丝 | 接收更新通知 |
ConcreteSubject | "代码侠"频道 | 具体的发布者 |
ConcreteObserver | 铁杆粉丝 | 具体的订阅者 |
🏰 第二章:魔法城堡 - 观察者模式的C++实现
🧙♂️ 魔法咒语:基础接口
cpp
/**
* 🎭 粉丝接口(观察者)
*
* 每个粉丝都必须实现"收到通知"这个方法
* 当博主发布新内容时,所有粉丝都会自动收到通知!
*/
class 粉丝 {
public:
virtual ~粉丝() = default;
/**
* @brief 收到新内容通知
*
* 就像手机弹出推送通知一样!
*
* @param 博主 谁发布的内容
* @param 内容 发布的具体内容
*/
virtual void 收到通知(const std::string& 博主,
const std::string& 内容) = 0;
/**
* @brief 粉丝名字
*/
virtual std::string 名字() const = 0;
};
/**
* 🎬 博主接口(主题)
*
* 博主需要管理粉丝列表,支持粉丝关注和取关
*/
class 博主 {
public:
virtual ~博主() = default;
/**
* @brief 新增粉丝(关注)
*
* @param 新粉丝 想要关注的新粉丝
*/
virtual void 新增粉丝(std::shared_ptr<粉丝> 新粉丝) = 0;
/**
* @brief 移除粉丝(取关)
*
* @param 旧粉丝 想要取关的粉丝
*/
virtual void 移除粉丝(std::shared_ptr<粉丝> 旧粉丝) = 0;
/**
* @brief 发布新内容
*
* 像魔法一样,自动通知所有粉丝!
*
* @param 内容 要发布的内容
*/
virtual void 发布内容(const std::string& 内容) = 0;
};
🏰 具体实现:网红博主的日常
cpp
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
#include <string>
/**
* 🎬 具体博主:代码侠频道
*
* 我们的网红博主,拥有百万粉丝!
* 使用weak_ptr管理粉丝,避免循环引用导致的内存泄漏
*/
class 代码侠频道 : public 博主 {
private:
std::string 频道名字_;
std::vector<std::weak_ptr<粉丝>> 粉丝列表_; // 🎯 关键技巧!
public:
/**
* @brief 构造函数
*
* @param 名字 频道名称
*/
代码侠频道(const std::string& 名字) : 频道名字_(名字) {
std::cout << "🎉 新频道诞生:《" << 名字 << "》上线啦!" << std::endl;
}
void 新增粉丝(std::shared_ptr<粉丝> 新粉丝) override {
// 检查是否已经关注
auto 是否存在 = std::find_if(粉丝列表_.begin(), 粉丝列表_.end(),
[&新粉丝](const std::weak_ptr<粉丝>& 弱粉丝) {
return !弱粉丝.expired() && 弱粉丝.lock() == 新粉丝;
});
if (是否存在 == 粉丝列表_.end()) {
粉丝列表_.emplace_back(新粉丝);
std::cout << "👏 《" << 新粉丝->名字() << "》关注了《"
<< 频道名字_ << "》!粉丝数:" << 粉丝数量() << std::endl;
}
}
void 移除粉丝(std::shared_ptr<粉丝> 旧粉丝) override {
auto 原来大小 = 粉丝列表_.size();
粉丝列表_.erase(
std::remove_if(粉丝列表_.begin(), 粉丝列表_.end(),
[&旧粉丝](const std::weak_ptr<粉丝>& 弱粉丝) {
return 弱粉丝.expired() || 弱粉丝.lock() == 旧粉丝;
}),
粉丝列表_.end());
if (粉丝列表_.size() < 原来大小) {
std::cout << "😢 《" << 旧粉丝->名字() << "》取关了《"
<< 频道名字_ << "》!粉丝数:" << 粉丝数量() << std::endl;
}
}
void 发布内容(const std::string& 内容) override {
std::cout << "\n🚀【" << 频道名字_ << "】发布新内容:" << 内容 << std::endl;
// 🧹 先清理失效的粉丝(比如注销账号的)
清理失效粉丝();
// 📢 然后通知所有有效粉丝
int 通知数量 = 0;
for (auto& 弱粉丝 : 粉丝列表_) {
if (auto 粉丝指针 = 弱粉丝.lock()) {
粉丝指针->收到通知(频道名字_, 内容);
通知数量++;
}
}
std::cout << "✅ 成功通知 " << 通知数量 << " 位粉丝!" << std::endl;
}
/**
* @brief 获取当前粉丝数量
*/
size_t 粉丝数量() const {
return std::count_if(粉丝列表_.begin(), 粉丝列表_.end(),
[](const std::weak_ptr<粉丝>& 弱粉丝) {
return !弱粉丝.expired();
});
}
private:
/**
* @brief 清理失效粉丝
*
* 就像定期清理不活跃用户一样
*/
void 清理失效粉丝() {
auto 原来数量 = 粉丝列表_.size();
粉丝列表_.erase(
std::remove_if(粉丝列表_.begin(), 粉丝列表_.end(),
[](const std::weak_ptr<粉丝>& 弱粉丝) {
return 弱粉丝.expired();
}),
粉丝列表_.end());
if (粉丝列表_.size() < 原来数量) {
std::cout << "🧹 清理了 " << (原来数量 - 粉丝列表_.size())
<< " 个失效粉丝" << std::endl;
}
}
};
/**
* 🎯 具体粉丝类型:普通粉丝
*
* 普通的视频观众,收到通知会很开心!
*/
class 普通粉丝 : public 粉丝, public std::enable_shared_from_this<普通粉丝> {
private:
std::string 粉丝名字_;
int 开心程度_; // 🎭 粉丝的情绪状态
public:
普通粉丝(const std::string& 名字) : 粉丝名字_(名字), 开心程度_(50) {}
void 收到通知(const std::string& 博主, const std::string& 内容) override {
开心程度_ += 10; // 收到通知更开心了!
std::cout << " 💌 " << 粉丝名字_ << " 收到《" << 博主
<< "》的推送:" << 内容 << std::endl;
std::cout << " 😊 开心程度:" << 开心程度_ << "/100" << std::endl;
// 随机决定是否点赞评论
if (开心程度_ > 70) {
std::cout << " 👍 忍不住点了个赞!" << std::endl;
}
}
std::string 名字() const override {
return 粉丝名字_;
}
/**
* @brief 获取当前开心程度
*/
int 获取开心程度() const { return 开心程度_; }
};
/**
* 🦸 具体粉丝类型:超级粉丝
*
* 铁杆粉丝,每次都会点赞、评论、转发三连!
*/
class 超级粉丝 : public 粉丝, public std::enable_shared_from_this<超级粉丝> {
private:
std::string 粉丝名字_;
int 三连次数_;
public:
超级粉丝(const std::string& 名字) : 粉丝名字_(名字), 三连次数_(0) {}
void 收到通知(const std::string& 博主, const std::string& 内容) override {
std::cout << " 🎯 " << 粉丝名字_ << " 收到偶像《" << 博主
<< "》的推送!" << std::endl;
// 自动三连!
std::cout << " 🔥 立即点赞、评论、转发三连!" << std::endl;
三连次数_++;
if (三连次数_ % 5 == 0) {
std::cout << " 🏆 达成 " << 三连次数_ << " 次三连成就!" << std::endl;
}
}
std::string 名字() const override {
return 粉丝名字_ + "(超级粉丝)";
}
};
🎪 魔法演示:博主的精彩日常
cpp
/**
* 🎪 主函数:网红博主的精彩日常
*
* 让我们看看观察者模式如何让内容发布变得如此轻松!
*/
int main() {
std::cout << "🌈 ===================================" << std::endl;
std::cout << "🌟 观察者模式大冒险:网红博主篇 🌟" << std::endl;
std::cout << "🌈 ===================================" << std::endl;
// 🎬 创建我们的网红博主
auto 代码侠 = std::make_shared<代码侠频道>("代码侠的编程乐园");
std::cout << "\n🎯 第一阶段:粉丝集结!" << std::endl;
// 👥 创建各种粉丝
auto 小明 = std::make_shared<普通粉丝>("小明");
auto 小红 = std::make_shared<普通粉丝>("小红");
auto 铁杆张 = std::make_shared<超级粉丝>("张大哥");
auto 技术李 = std::make_shared<超级粉丝>("李大师");
// ➕ 粉丝关注博主
代码侠->新增粉丝(小明);
代码侠->新增粉丝(小红);
代码侠->新增粉丝(铁杆张);
代码侠->新增粉丝(技术李);
std::cout << "\n📊 当前粉丝统计:" << 代码侠->粉丝数量() << " 人" << std::endl;
std::cout << "\n🎬 第二阶段:内容发布狂欢!" << std::endl;
// 🚀 博主发布内容,自动通知所有粉丝!
代码侠->发布内容("C++设计模式入门教程");
代码侠->发布内容("观察者模式的实战应用");
代码侠->发布内容("智能指针的深度解析");
std::cout << "\n🔄 第三阶段:粉丝动态变化" << std::endl;
// 小红取关了 😢
代码侠->移除粉丝(小红);
// 新粉丝加入 🎉
auto 新粉丝 = std::make_shared<普通粉丝>("新手小王");
代码侠->新增粉丝(新粉丝);
std::cout << "\n📊 更新后粉丝统计:" << 代码侠->粉丝数量() << " 人" << std::endl;
// 继续发布内容
代码侠->发布内容("现代C++新特性介绍");
std::cout << "\n🧪 第四阶段:智能指针魔法测试" << std::endl;
{
// 临时粉丝,测试生命周期管理
auto 临时粉丝 = std::make_shared<普通粉丝>("临时观众");
代码侠->新增粉丝(临时粉丝);
std::cout << "📈 临时粉丝加入后:" << 代码侠->粉丝数量() << " 人" << std::endl;
// 临时粉丝离开作用域,自动销毁
}
// 🎯 这里不会崩溃!weak_ptr自动处理了失效粉丝
代码侠->发布内容("高级主题:元编程入门");
std::cout << "📉 临时粉丝离开后:" << 代码侠->粉丝数量() << " 人" << std::endl;
std::cout << "\n🎊 ===================================" << std::endl;
std::cout << "🎉 演示圆满结束! 🎉" << std::endl;
std::cout << "🎊 ===================================" << std::endl;
return 0;
}
运行结果预览:
🌈 ===================================
🌟 观察者模式大冒险:网红博主篇 🌟
🌈 ===================================
🎯 第一阶段:粉丝集结!
🎉 新频道诞生:《代码侠的编程乐园》上线啦!
👏 《小明》关注了《代码侠的编程乐园》!粉丝数:1
👏 《小红》关注了《代码侠的编程乐园》!粉丝数:2
👏 《张大哥(超级粉丝)》关注了《代码侠的编程乐园》!粉丝数:3
👏 《李大师(超级粉丝)》关注了《代码侠的编程乐园》!粉丝数:4
🎬 第二阶段:内容发布狂欢!
🚀【代码侠的编程乐园】发布新内容:C++设计模式入门教程
💌 小明 收到《代码侠的编程乐园》的推送:C++设计模式入门教程
😊 开心程度:60/100
💌 小红 收到《代码侠的编程乐园》的推送:C++设计模式入门教程
😊 开心程度:60/100
🎯 张大哥(超级粉丝) 收到偶像《代码侠的编程乐园》的推送!
🔥 立即点赞、评论、转发三连!
🎯 李大师(超级粉丝) 收到偶像《代码侠的编程乐园》的推送!
🔥 立即点赞、评论、转发三连!
✅ 成功通知 4 位粉丝!
🗺️ 第三章:冒险地图 - 观察者模式的多种应用
🏦 场景一:股票市场监控系统
想象你是个股民,同时监控多支股票:
cpp
/**
* 📈 股民观察者
*
* 就像手机上的股票APP,股价变动立即推送!
*/
class 股民 : public 粉丝 {
private:
std::string 名字_;
double 买入阈值_;
double 卖出阈值_;
public:
股民(const std::string& 名字, double 买入价, double 卖出价)
: 名字_(名字), 买入阈值_(买入价), 卖出阈值_(卖出价) {}
void 收到通知(const std::string& 股票, const std::string& 价格文本) override {
double 价格 = std::stod(价格文本);
std::cout << "📱 " << 名字_ << " 收到 " << 股票
<< " 价格警报:" << 价格 << "元" << std::endl;
if (价格 <= 买入阈值_) {
std::cout << " 💰 机会!低于买入价,果断加仓!" << std::endl;
} else if (价格 >= 卖出阈值_) {
std::cout << " 💸 警惕!高于卖出价,考虑减仓!" << std::endl;
} else {
std::cout << " ⏳ 价格在区间内,继续观望..." << std::endl;
}
}
std::string 名字() const override { return 名字_ + "(股民)"; }
};
🎮 场景二:游戏成就系统
想象你在玩游戏,自动解锁各种成就:
cpp
/**
* 🏆 成就系统观察者
*
* 像游戏里的成就系统,自动检测玩家行为并解锁成就!
*/
class 成就系统 : public 粉丝 {
private:
std::vector<std::string> 已解锁成就_;
public:
void 收到通知(const std::string& 事件类型, const std::string& 事件内容) override {
std::cout << "🎮 成就系统检测到事件:" << 事件类型
<< " - " << 事件内容 << std::endl;
if (事件类型 == "首次登录" && !成就已解锁("欢迎来到游戏")) {
解锁成就("欢迎来到游戏");
} else if (事件类型 == "击败Boss" && !成就已解锁("Boss杀手")) {
解锁成就("Boss杀手");
} else if (事件类型 == "收集完成" && !成就已解锁("收藏家")) {
解锁成就("收藏家");
}
}
std::string 名字() const override { return "成就系统"; }
private:
void 解锁成就(const std::string& 成就名) {
已解锁成就_.push_back(成就名);
std::cout << " 🏆 成就解锁:《" << 成就名 << "》!" << std::endl;
}
bool 成就已解锁(const std::string& 成就名) {
return std::find(已解锁成就_.begin(), 已解锁成就_.end(), 成就名)
!= 已解锁成就_.end();
}
};
🛠️ 第四章:魔法工具 - 完整的项目配置
📦 Makefile:构建我们的魔法世界
makefile
# 🎯 编译器配置
CXX := g++
CXX_STD := c++17
# 🚀 编译选项
CXXFLAGS := -std=$(CXX_STD) -Wall -Wextra -Wpedantic -O2
DEBUG_FLAGS := -g -DDEBUG -O0
RELEASE_FLAGS := -DNDEBUG -O3
# 🎪 目标文件
TARGETS := 网红博主演示 股票监控演示 游戏成就演示
# 🏗️ 默认构建所有目标
all: $(TARGETS)
# 🎬 网红博主演示
网红博主演示: 观察者模式_博主示例.cpp
$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<
# 📈 股票监控演示
股票监控演示: 观察者模式_股票示例.cpp
$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<
# 🎮 游戏成就演示
游戏成就演示: 观察者模式_游戏示例.cpp
$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<
# 🐛 调试版本
debug: CXXFLAGS += $(DEBUG_FLAGS)
debug: $(TARGETS)
# 🧹 清理构建文件
clean:
rm -f $(TARGETS) *.o
# 📥 安装依赖
install-deps:
sudo apt-get update
sudo apt-get install -y g++ build-essential
# 🧪 运行所有测试
test: all
@echo "🎬 运行网红博主演示..."
@./网红博主演示
@echo
@echo "📈 运行股票监控演示..."
@./股票监控演示
@echo
@echo "🎮 运行游戏成就演示..."
@./游戏成就演示
.PHONY: all clean debug install-deps test
🎯 操作指南:启动我们的冒险!
编译命令:
bash
# 安装魔法工具
make install-deps
# 构建所有演示
make
# 或者直接运行测试
make test
运行示例:
bash
# 运行网红博主演示
./网红博主演示
# 运行结果类似:
# 🌈 ===================================
# 🌟 观察者模式大冒险:网红博主篇 🌟
# 🌈 ===================================
# 🎯 第一阶段:粉丝集结!
# 🎉 新频道诞生:《代码侠的编程乐园》上线啦!
# 👏 《小明》关注了《代码侠的编程乐园》!粉丝数:1
# ... (精彩继续)
🎓 第五章:智慧总结 - 观察者模式的精髓
🏆 设计模式的胜利
通过这场冒险,我们学到了:
🎯 观察者模式的核心优势:
- 自动通知:像魔法一样,主题变化自动通知所有观察者
- 松耦合:博主不知道粉丝的具体类型,只知道他们能收通知
- 动态管理:运行时可以随意添加、删除观察者
- 安全可靠 :使用
weak_ptr
避免内存泄漏和悬空指针
🛡️ 生命周期管理的智慧:
cpp
// ❌ 危险做法:原始指针
std::vector<粉丝*> 粉丝列表_; // 粉丝销毁后会导致崩溃!
// ✅ 安全做法:weak_ptr
std::vector<std::weak_ptr<粉丝>> 粉丝列表_; // 自动处理失效粉丝
🚀 实际应用场景:
- 📱 APP推送通知系统
- 📈 金融市场价格监控
- 🎮 游戏事件处理系统
- 🔧 配置热更新系统
- 🌐 微服务消息总线
🎭 故事寓意
观察者模式就像现实世界中的订阅机制:
- 博主发布内容 → 主题状态改变
- 粉丝接收通知 → 观察者更新
- 关注/取关 → 动态注册/注销
这种"发布-订阅"的思维,让我们的代码世界变得更加智能 和灵活!
🎊 冒险家,恭喜你! 你已经掌握了观察者模式的精髓。现在就去用这个魔法,构建更加智能、灵活的软件系统吧!记住:好的设计模式,让代码世界变得更加美好!✨