05_Observer观察者模式-学习笔记

C++设计模式

整理自李建忠老师的《C++设计模式》视频教程



Observer观察者模式(常用)(组件协作模式)


“组件协作”模式:

现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。

  • 典型模式
    • Template Method
    • Strategy
    • Observer / Event

1. 动机(Motivation)

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

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


2. 模式定义

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


3. 结构(Structure)

在这里插入图片描述


4. 代码

文件分割器

4.1代码1
//--------------------------class MainForm---------------------------------//

class MainForm : public Form
{
    TextBox *txtFilePath;		//大文件的路径
    TextBox *txtFileNumber;		//分成几份
    
    
    ProgressBar *progressBar; 	//观察者 //如:进度条显示
	
    
public:
    
    void Button1_Click()
    {
		//收集参数信息
        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());
		//参数传递给FileSplitter
        FileSplitter splitter(filePath, number, progressBar); //传递给下一级
		//调用FileSplitter的split()方法
        splitter.split();
    }
    
};

//--------------------------class FileSplitter---------------------------------//

class FileSplitter
{
    string m_filePath;
    int m_fileNumber;
    
    //此处违背了:依赖倒置原则
    //    此处的实现细节,应该依赖于抽象
    ProgressBar *m_progressBar; //依赖于上一级,所以要解决这种依赖关系
	//具体的通知控件
    
public:
    
    FileSplitter(const string &filePath,
                 int fileNumber,
                 ProgressBar *progressBar)
        : m_filePath(filePath),
          m_fileNumber(fileNumber),
          m_progressBar(progressBar)
    {
    }

    void split()
    {

        //1.分批次读取大文件

        //2.分批次向小文件中写入
        for (int i = 0; i < m_fileNumber; i++)
        {
            //...
            float progressValue = m_fileNumber;
            progressValue = (i + 1) / progressValue;
            m_progressBar->setValue(progressValue);//更新进度条
        }
    }
};

//-----------------------------------------------------------//


4.2改进后的C++伪代码
  • 01-依赖倒置原则(DIP)

    • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。
    • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。
  • 什么是依赖(编译时依赖)?

    • A依赖B,A编译时B必须存在才能编译通过
//-----------------------class IProgress------class MainForm------------------------------//

//观察者 - 基类
class IProgress
{
public:
    virtual void DoProgress(float value) = 0;
    virtual ~IProgress() {}
};

// MainForm 是一个观察者 - 子类
class MainForm : public Form, public IProgress
{
    TextBox *txtFilePath;
    TextBox *txtFileNumber;
    
public:
    void Button1_Click()
    {

        string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());

        ConsoleNotifier cn;

        //此class的对象负责:分隔文件为几份
        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 << ".";
    }
};

//--------------------------class FileSplitter---------------------------------//

class FileSplitter
{
    string m_filePath;
    int m_fileNumber;
	
    //ProgressBar *progressBar;//具体的通知控件
    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); //给观察者-发送通知
        }
    }
    
public:
    void addIProgress(IProgress *iprogress) //添加观察者,添加订阅
    {
        m_iprogressList.push_back(iprogress);
    }

    void removeIProgress(IProgress *iprogress) //移除某个观察者
    {
        m_iprogressList.remove(iprogress);
    }

protected:
    virtual void onProgress(float value){ // 给观察者-发送通知
        List<IProgress *>::iterator itor = m_iprogressList.begin();
        while (itor != m_iprogressList.end()){
            (*itor)->DoProgress(value); //更新进度条
        	itor++;
        }
    }
};

//-----------------------------------------------------------//

C++不推荐多继承

只推荐一种多继承

一个是主要的基类,其它的都是接口(抽象基类)


5. 要点总结

  • 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播
  • 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
  • Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值