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

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

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

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

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

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推送通知系统
  • 📈 金融市场价格监控
  • 🎮 游戏事件处理系统
  • 🔧 配置热更新系统
  • 🌐 微服务消息总线

🎭 故事寓意

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

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

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


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

相关推荐
charlie1145141912 小时前
精读 C++20 设计模式:行为型设计模式 — 备忘录模式
c++·学习·设计模式·c++20·备忘录模式
陈鹏鹏勇闯天涯3 小时前
C++智能指针
c++
希望_睿智3 小时前
实战设计模式之迭代器模式
c++·设计模式·架构
charlie1145141913 小时前
精读C++20设计模式——行为型设计模式:策略模式
c++·学习·设计模式·策略模式·c++20
郝学胜-神的一滴3 小时前
深入理解 Qt 元对象系统:QMetaEnum 的应用与实践
开发语言·c++·qt·软件工程
艾莉丝努力练剑3 小时前
【C++STL :vector类 (二) 】攻克 C++ Vector 的迭代器失效陷阱:从源码层面详解原理与解决方案
linux·开发语言·c++·经验分享
hope_wisdom3 小时前
C/C++数据结构之用数组实现栈
c语言·数据结构·c++·数组·
QUST-Learn3D5 小时前
C++单头文件实现windows进程间通信(基于命名管道)
c++·windows·单片机
磨十三12 小时前
C++ 标准库排序算法 std::sort 使用详解
开发语言·c++·排序算法