首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

设计模式之观察者模式(学习笔记)

编程知识
2024年07月21日 16:00

定义

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。这种模式用于实现对象之间的解耦,使得一个对象的变化可以通知并更新多个依赖对象,而无需直接引用它们。

为什么使用观察者模式?

  1. 解耦

    • 观察者模式将观察者(Observer)和被观察者(Subject)解耦,两个对象之间的依赖关系被转化为依赖接口。
  2. 动态增加观察者

    • 观察者模式允许在运行时动态增加或删除观察者,灵活性更高。
  3. 维护简便

    • 当被观察者的状态发生变化时,所有依赖对象自动更新,无需手动管理这些依赖关系,简化了维护。

实现步骤

  1. 定义抽象主题类

    • 定义一个抽象主题类,声明添加、删除观察者的方法,以及通知所有观察者的方法。
  2. 实现具体主题类

    • 实现具体主题类,包含状态变化时的通知逻辑。
  3. 定义抽象观察者类

    • 定义一个抽象观察者类,声明更新方法,具体观察者实现这个方法来更新自身状态。
  4. 实现具体观察者类

    • 实现具体观察者类,实现更新方法,根据被观察者的变化更新自身状态。

优缺点和适用场景

优点

  1. 解耦

    • 观察者与被观察者之间的耦合度降低,代码更加模块化和易于维护。
  2. 灵活性

    • 可以动态地增加或删除观察者,灵活地响应需求变化。
  3. 自动更新

    • 被观察者状态变化时,所有观察者都会自动收到通知并更新状态,无需手动调用。

缺点

  1. 可能导致性能问题

    • 如果有大量观察者,通知所有观察者可能会导致性能开销。
  2. 调试困难

    • 观察者模式涉及多个对象之间的通知和更新,可能会导致调试变得复杂。

适用场景

  1. 事件处理系统

    • 例如 GUI 事件处理系统,当用户触发某个事件时,所有相关的处理函数都需要被通知。
  2. 模型-视图-控制器(MVC)架构

    • 模型(Model)作为被观察者,视图(View)作为观察者,当模型的数据发生变化时,视图会自动更新显示。
  3. 订阅-发布系统

    • 允许对象订阅事件并在事件发生时接收通知,实现松耦合的事件处理机制。

例子:股票市场

假设我们有一个股票市场系统,当某只股票的价格变化时,所有关注这只股票的投资者都会收到通知。我们可以使用观察者模式来实现这一功能。
#include <iostream>
#include <vector>
#include <memory>

/*
在这个例子中,当股票价格发生变化时,所有的投资者都会收到通知并更新他们的状态。观察者模式使得我们可以轻松地添加或删除投资者,而不需要修改股票类的代码,从而实现了解耦和灵活性。
*/

// 抽象观察者类:投资者
class Investor {
public:
    virtual ~Investor() {}
    virtual void update(double price) = 0;
};


// 具体观察者类:具体投资者
class ConcreteInvestor : public Investor {
private:
    std::string name;
public:
    ConcreteInvestor(const std::string& name) : name(name) {}
    void update(double price) override {
        std::cout << "Investor " << name << " is notified. New stock price: " << price << std::endl;
    }
};


// 抽象主题类:股票
class Stock {
public:
    virtual ~Stock() {}
    virtual void addObserver(std::shared_ptr<Investor> investor) = 0;
    virtual void removeObserver(std::shared_ptr<Investor> investor) = 0;
    virtual void notifyObservers() = 0;
};


// 具体主题类:具体股票
class ConcreteStock : public Stock {
private:
    std::vector<std::shared_ptr<Investor>> investors;
    double price;
public:
    void addObserver(std::shared_ptr<Investor> investor) override {
        investors.push_back(investor);
    }
    void removeObserver(std::shared_ptr<Investor> investor) override {
        investors.erase(std::remove(investors.begin(), investors.end(), investor), investors.end());
    }
    void notifyObservers() override {
        for (const auto& investor : investors) {
            investor->update(price);
        }
    }
    void setPrice(double newPrice) {
        price = newPrice;
        notifyObservers();
    }
};


int main() {
    // 创建具体股票
    std::shared_ptr<ConcreteStock> stock = std::make_shared<ConcreteStock>();


    // 创建具体投资者
    std::shared_ptr<Investor> investor1 = std::make_shared<ConcreteInvestor>("Alice");
    std::shared_ptr<Investor> investor2 = std::make_shared<ConcreteInvestor>("Bob");


    // 添加投资者到股票的观察者列表中
    stock->addObserver(investor1);
    stock->addObserver(investor2);


    // 修改股票价格
    stock->setPrice(100.0);
    stock->setPrice(105.0);


    return 0;
}

 

From:https://www.cnblogs.com/best-doraemon/p/18314669
本文地址: http://www.shuzixingkong.net/article/249
0评论
提交 加载更多评论
其他文章 深入探究 Golang 反射:功能与原理及应用
Go 出于通用性的考量,提供了反射这一功能。借助反射功能,我们可以实现通用性更强的函数,传入任意的参数,在函数内通过反射动态调用参数对象的方法并访问它的属性。本文将深入探讨Golang反射包reflect的功能和原理。同时,我们学习某种东西,一方面是为了实践运用,另一方面则是出于功利性面试的目的。所
深入探究 Golang 反射:功能与原理及应用 深入探究 Golang 反射:功能与原理及应用 深入探究 Golang 反射:功能与原理及应用
Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文
title: Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文 date: 2024/7/21 updated: 2024/7/21 author: cmdragon excerpt: 摘要:“Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文”介绍了Nuxt 3中useN
Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文 Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文
一文揭开JDK21虚拟线程的神秘面纱
虚拟线程快速体验 环境:JDK21 + IDEA public static void main(String[] args) { try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0
番外2: go语言写的简要数据同步工具
go-etl工具 作为go-etl工具的作者,想要安利一下这个小巧的数据同步工具,它在同步百万级别的数据时表现极为优异,基本能在几分钟完成数据同步。 1.它能干什么的? go-etl是一个数据同步工具集,目前支持MySQL,postgres,oracle,SQL SERVER,DB2等主流关系型数据
番外2: go语言写的简要数据同步工具 番外2: go语言写的简要数据同步工具 番外2: go语言写的简要数据同步工具
试试这个工作流引擎吧,还有个简洁美观的流程设计器
ruoyi-flow若依工作流引擎设计器一个简洁轻量的工作流引擎。 java工作流引擎,真正的国产工作流引擎,前后端代码完整且还拥有一个简洁美观的流程设计器。 前端vue后端Java的。 功能特点 1、使用json存储流程模板 2、支持驳回、拿回等 3、支持状态配置、权限配置 4、支持条件分支 流程
试试这个工作流引擎吧,还有个简洁美观的流程设计器 试试这个工作流引擎吧,还有个简洁美观的流程设计器 试试这个工作流引擎吧,还有个简洁美观的流程设计器
ComfyUI进阶:Comfyroll插件 (六)
ComfyUI进阶:Comfyroll插件 (六)前言:学习ComfyUI是一场持久战,而Comfyroll 是一款功能强大的自定义节点集合,专为 ComfyUI 用户打造,旨在提供更加丰富和专业的图像生成与编辑工具。借助这些节点,用户可以在静态图像的精细调整和动态动画的复杂构建方面进行深入探索。C
ComfyUI进阶:Comfyroll插件 (六) ComfyUI进阶:Comfyroll插件 (六) ComfyUI进阶:Comfyroll插件 (六)
方法引用
方法引用有什么用? 写更少代码 提高代码复用性和可维护性(尤其是团队项目中) 引用静态方法如果你要引用的是一个静态方法,你可以使用类名::静态方法的形式。例如, 将集合中String类型数据转换成int类型这是匿名内部类的写法:查看parsInt源码可以发现该方法满足静态方法引用的条件.因此可以直接
方法引用 方法引用 方法引用
一文全懂:独立冗余磁盘阵列(RAID)
本文介绍了独立冗余磁盘阵列RAID的四种常见模式:RAID0、RAID1、RAID5、RAID10,并且使用mdadm命令在liux系统上如何创建磁盘阵列的详细步骤。
一文全懂:独立冗余磁盘阵列(RAID) 一文全懂:独立冗余磁盘阵列(RAID) 一文全懂:独立冗余磁盘阵列(RAID)