当前位置: 首页 > news >正文

设计模式(C++)详解——观察者模式(Observer)(2)

🌟 观察者模式大冒险:一场代码世界的订阅狂欢!🌟

想象一下,你是一个网红博主,每次发布新视频,你的百万粉丝都会立即收到通知!这就是观察者模式的魔力!

🎭 第一章:缘起 - 一个网红博主的烦恼

从前,有个叫"代码侠"的博主,他每次发布新视频都要手动通知每个粉丝:

// 糟糕的旧时代
void 发布视频() {制作视频();打电话通知(粉丝1);  // 😫 累死了!打电话通知(粉丝2);发邮件通知(粉丝3);// ... 还有99997个粉丝要通知
}

"代码侠"累得筋疲力尽,直到他遇到了观察者模式这个魔法!

🎯 核心概念:订阅的魔力

// 新时代的智慧
void 发布视频() {制作视频();一键通知所有粉丝();  // 🎉 太轻松了!
}

观察者模式的四大天王

角色现实比喻职责
Subject(主题)网红博主维护粉丝列表,发布内容
Observer(观察者)粉丝接收更新通知
ConcreteSubject"代码侠"频道具体的发布者
ConcreteObserver铁杆粉丝具体的订阅者

🏰 第二章:魔法城堡 - 观察者模式的C++实现

🧙‍♂️ 魔法咒语:基础接口

/*** 🎭 粉丝接口(观察者)* * 每个粉丝都必须实现"收到通知"这个方法* 当博主发布新内容时,所有粉丝都会自动收到通知!*/
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;
};

🏰 具体实现:网红博主的日常

#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 粉丝名字_ + "(超级粉丝)";}
};

🎪 魔法演示:博主的精彩日常

/*** 🎪 主函数:网红博主的精彩日常* * 让我们看看观察者模式如何让内容发布变得如此轻松!*/
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 位粉丝!

🗺️ 第三章:冒险地图 - 观察者模式的多种应用

🏦 场景一:股票市场监控系统

想象你是个股民,同时监控多支股票:

/*** 📈 股民观察者* * 就像手机上的股票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 名字_ + "(股民)"; }
};

🎮 场景二:游戏成就系统

想象你在玩游戏,自动解锁各种成就:

/*** 🏆 成就系统观察者* * 像游戏里的成就系统,自动检测玩家行为并解锁成就!*/
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:构建我们的魔法世界

# 🎯 编译器配置
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 updatesudo apt-get install -y g++ build-essential# 🧪 运行所有测试
test: all@echo "🎬 运行网红博主演示..."@./网红博主演示@echo@echo "📈 运行股票监控演示..."@./股票监控演示  @echo@echo "🎮 运行游戏成就演示..."@./游戏成就演示.PHONY: all clean debug install-deps test

🎯 操作指南:启动我们的冒险!

编译命令

# 安装魔法工具
make install-deps# 构建所有演示
make# 或者直接运行测试
make test

运行示例

# 运行网红博主演示
./网红博主演示# 运行结果类似:
# 🌈 ===================================
# 🌟   观察者模式大冒险:网红博主篇   🌟
# 🌈 ===================================
# 🎯 第一阶段:粉丝集结!
# 🎉 新频道诞生:《代码侠的编程乐园》上线啦!
# 👏 《小明》关注了《代码侠的编程乐园》!粉丝数:1
# ... (精彩继续)

🎓 第五章:智慧总结 - 观察者模式的精髓

🏆 设计模式的胜利

通过这场冒险,我们学到了:

🎯 观察者模式的核心优势

  • 自动通知:像魔法一样,主题变化自动通知所有观察者
  • 松耦合:博主不知道粉丝的具体类型,只知道他们能收通知
  • 动态管理:运行时可以随意添加、删除观察者
  • 安全可靠:使用weak_ptr避免内存泄漏和悬空指针

🛡️ 生命周期管理的智慧

// ❌ 危险做法:原始指针
std::vector<粉丝*> 粉丝列表_;  // 粉丝销毁后会导致崩溃!// ✅ 安全做法:weak_ptr  
std::vector<std::weak_ptr<粉丝>> 粉丝列表_;  // 自动处理失效粉丝

🚀 实际应用场景

  • 📱 APP推送通知系统
  • 📈 金融市场价格监控
  • 🎮 游戏事件处理系统
  • 🔧 配置热更新系统
  • 🌐 微服务消息总线

🎭 故事寓意

观察者模式就像现实世界中的订阅机制

  • 博主发布内容 → 主题状态改变
  • 粉丝接收通知 → 观察者更新
  • 关注/取关 → 动态注册/注销

这种"发布-订阅"的思维,让我们的代码世界变得更加智能灵活


🎊 冒险家,恭喜你! 你已经掌握了观察者模式的精髓。现在就去用这个魔法,构建更加智能、灵活的软件系统吧!记住:好的设计模式,让代码世界变得更加美好!✨

http://www.dtcms.com/a/426917.html

相关文章:

  • LeetCode 392 判断子序列
  • 树的存储结构
  • 2025年9月GESP(C++三级):数组清零
  • 怎样查看网站建设时间注册公司需要什么费用
  • Deepoc具身模型外拓板:重塑居家服务机器人的交互革命
  • cpuset v1
  • 2025年9月个人工作生活总结
  • Java SE “JDK1.8新特性”面试清单(含超通俗生活案例与深度理解)
  • 站台建筑资阳网站推广
  • 【论文阅读 | ECCV 2024 | DAMSDet:具有竞争性查询选择与自适应特征融合的动态自适应多光谱检测变换器】
  • 企业网站 三网系统好玩有趣的网站
  • 小程序的页面宽度 设置多少合适??
  • 基于libwebsockets与cJson的ASR Server实时语音识别实现指南
  • golang 写路由的时候要注意
  • EXCEL哪个版本开始支持VSTO-office插件?
  • 盲盒抽卡机小程序的技术挑战与解决方案
  • 全网网站建设推广国外设计网站都有哪些
  • 零基础学AI大模型之LangChain聊天模型多案例实战
  • GPU 网络基础,Part 2(MoE 训练中的网络挑战;什么是前、后端网络;什么是东西向、南北向流量)
  • 【菜狗学聚类】序列嵌入表示、UMAP降维——20250930
  • 网站外链建设的八大基本准则东大桥做网站的公司
  • MySQL进阶知识点(八)---- SQL优化
  • 【C++STL :vector类 (二) 】攻克 C++ Vector 的迭代器失效陷阱:从源码层面详解原理与解决方案
  • C++ string类常用操作
  • 修改网站模板详解如何开网站需要多少钱
  • 浅谈WebSocket
  • 做网站背景wordpress登录样式
  • 自动化通信谜团:耐达讯自动化Modbus RTU如何变身 Profibus连接触摸屏
  • 调节阀控制的“语言障碍“:耐达讯自动化一招破解,让Modbus RTU变身Profibus!
  • LE AUDIO之助听器Hearing Access Profile