首页 > 其他分享 >Observer(观察者)

Observer(观察者)

时间:2024-08-07 15:56:59浏览次数:14  
标签:Observer filePath int void 观察者 IProgress fileNumber public

1. 意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于 它的对象都得到通知并被自动更新 。

2. 动机

在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。

使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

3. 示例

假设我们需要实现一个文件分割器,在split函数里面实现大文件的分割。

class FileSplitter {
    string m_filePath;
    int m_fileNumber;
 
public:
    FileSplitter(const string& filePath, int fileNumber) :
        m_filePath(filePath), m_fileNumber(fileNumber) {}
 
    void split(){
        // 1.读取大文件
        // 2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++) {
                        //...
        }
    }
};

我们新建一个窗口,定义在一个按钮被点击时开始使用文件分割器类分割文件。

class MainForm : public Form {
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
 
public:
    void Button1_Click(){
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());
        FileSplitter splitter(filePath, number);
        splitter.split();
    }
};

如果我们想在窗口中添加一个进度条,那么我们需要在窗口类和文件分割器类都中添加一个类型为ProgressBar*的进度条成员,并向文件分割器类的构造函数添加构造这个进度条的方法,修改split函数使其支持显示进度条。

这么做违背了依赖倒置原则,ProgressBar进度条属于实现细节,后期如需更换进度条为其它组件需要再次修改这些代码。我们可以使用一种抽象的方式表达ProgressBar进度条这样一种通知,通过定义一个接口来决定通知的形式。

class IProgress{
public:
    virtual void DoProgress(float value) = 0;
    virtual ~IProgress() {}
};
 
class FileSplitter
{
    string m_filePath;
    int m_fileNumber;
    //ProgressBar* m_progressBar; // 通知控件
    IProgress* m_iprogress;  // 抽象通知机制
  
public:
    FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress;) :
        m_filePath(filePath), m_fileNumber(fileNumber), m_iprogress(iprogress){}
 
    void split(){
        // 1.读取大文件
        // 2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++){
            //...
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            m_iprogress->DoProgress(progressValue); // 更新进度条
        }
    }
};

然后我们再让窗口类继承这个接口,重写DoProgress函数,并将this指针传递给分割器的构造函数。

class MainForm : public Form, public IProgress
{
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;
 
public:
    void Button1_Click(){
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());
        FileSplitter splitter(filePath, number, this);
        splitter.split();
    }
    
    virtual void DoProgress(float value) {
        progressBar->setValue(value);
    }
};

我们可以继续修改分割器类,使其可以通知多个组件。首先,我们创建元素类型为IProgress*的m_iprogressList列表。其次,我们添加加入和删除通知机制的函数。最后,将通知行为代码抽象为onProgress函数,每次都通知列表中的所有元素。

class IProgress {
public:
    virtual void DoProgress(float value) = 0;
    virtual ~IProgress() {}
};
​
class FileSplitter {
    string m_filePath;
    int m_fileNumber;
    list<IProgress*>  m_iprogressList; // 抽象通知机制,支持多个观察者
    
public:
    FileSplitter(const string& filePath, int fileNumber) :
        m_filePath(filePath), 
        m_fileNumber(fileNumber) {}
 
    void split() {
        // 1.读取大文件
        // 2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++){
            //...
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            onProgress(progressValue);//发送通知
        }
    }
 
    void addIProgress(IProgress* iprogress){
        m_iprogressList.add(iprogress);
    }
 
    void removeIProgress(IProgress* iprogress){
        m_iprogressList.remove(iprogress);
    }
 
protected:
    virtual void onProgress(float value){
        for (auto item : m_iprogressList) {
                item->DoProgress(value);
        }
    }
};

这样我们的分割器就可以支持更新多个组件。窗口类也需做对应的修改。

class MainForm : public Form, public IProgress {
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;
 
public:
    void Button1_Click(){
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());
        ConsoleNotifier cn;
        FileSplitter splitter(filePath, number);
        splitter.addIProgress(this); //订阅通知
        splitter.addIProgress(&cn); //订阅通知
        splitter.split();
        splitter.removeIProgress(this);
    }
  
    virtual void DoProgress(float value){
        progressBar->setValue(value);
    }
};
 
// 第二个观察者
class ConsoleNotifier : public IProgress {
public:
    virtual void DoProgress(float value){
        cout << ".";
    }
};

我们也可以将addIProgress、removeIProgress和onProgress函数以及m_iprogressList成员放到基类中,这里就不给出代码示例了。

4. 结构

5. 定义

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

专属学习链接:https://xxetb.xetslk.com/s/32bi94

6. 要点总结

使用面向对象的抽象,Observer模式使得我们可以独立地改变目标(被观察者)与观察者,从而使二者之间的依赖关系达致松耦合。

目标(被观察者)发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。

观察者自己决定是否需要订阅通知,目标对象对此一无所知。

Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。

标签:Observer,filePath,int,void,观察者,IProgress,fileNumber,public
From: https://blog.csdn.net/dswwedxc/article/details/140993773

相关文章

  • springBoot中的观察者模式实现
    pringBoot中的观察者模式实现什么是观察者模式?假设你要打一局酣畅淋漓的游戏,那么你可能需要进行以下操作->1.解锁手机屏幕2.找到你要进行游戏的位置3.单机启动游戏现在我们将启动游戏设计为一个api接口控制器类->三个业务方法均为控制台打印日志访问接口-......
  • Dom-API | MutationObserver使用方法详解
    MutationObserver介绍MutationObserver是是一个用于监视DOM变动的WebAPI。通过它可以监控DOM树中的更改,比如元素的属性、子元素的增加和删除等,并在这些变化发生时执行回调函数。可以替代过时的MutationEvents,它具有更高的性能和更广的适用性。使用步骤详细说明1.创......
  • 实现一个类似c#LINQ链式语法的观察者模式
    文章目录前言一、UniRx与LINQ的关系二、实现原理三、核心代码实现1.接口部分2.Disposable3.主题(事件源)4.操作符5.观察者6.操作符扩展使用示例1.带参数主题2.不带参数主题总结前言最近在学习UniRx的源码,其中链式语法的实现引起了我的兴趣,结合LINQ的实现原理,想要......
  • getBoundingClientRect 和 IntersectionObserver 的区别和用法
    目录getBoundingClientRectIntersectionObservergetBoundingClientRectgetBoundingClientRect是一个DOMAPI方法,用于获取指定元素相对于视口的位置和尺寸信息。它返回一个DOMRect对象,包含了元素的左上角和右下角相对于视口的坐标。“图片懒加载”,这个词语想必大家再熟悉不......
  • 设计模式C++003__观察者模式
    设计模式C++003__观察者模式1、动机:在软件构建过程中,我们需要为某些对象建立一种“通过依赖关系”--一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使得软件不好抵御变化。?使用面向对象技术,可以将这种依赖关系弱化,并形成......
  • JS实现一个发布-订阅模式(观察者模式)
    //创建一个简单的事件中心,允许对象订阅事件、发布事件并取消订阅。//创建一个事件中心对象,IIFE,返回一个包含订阅、取消订阅和发布方法的对象constEventCenter=(function(){//存储事件及其对应的订阅者,存储事件及其对应的监听器数组constevents={}......
  • SplObserver 和 SplSubject 接口实现观察者模式
    <?phpclassSubjectimplementsSplSubject{private$observers=[];private$state;publicfunctionattach(SplObserver$observer){$this->observers[]=$observer;}publicfunctiondetach(SplObserver$observer){......
  • 职责链、命令和观察者设计模式的区别
    职责链、命令和观察者是三种不同的设计模式,它们各自解决不同类型的问题。下面分别介绍这三种设计模式的特点和区别:1.职责链模式(ChainofResponsibility)定义:职责链模式是一种行为设计模式,它通过将请求的处理者组织成一个链,使得请求可以沿这条链传递,直到有一个处理者处理......
  • 设计模式之观察者模式(学习笔记)
    定义观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。这种模式用于实现对象之间的解耦,使得一个对象的变化可以通知并更新多个依赖对象,而无需直接引用它们。为什么使用观察者模式?解耦观......
  • 观察者模式实战:Spring Boot中联动更新机制的优雅实现
    引言在许多应用系统中,我们经常需要处理多个表之间的关联更新问题。例如,在教育管理系统中,当学生的基本信息表中的年龄字段发生更改时,我们可能还需要同步更新学生档案表和学生成绩表中的相关信息。本文将通过一个具体的案例,介绍如何在SpringBoot项目中利用观察者模式来优雅地解......