设计模式-观察者模式

发布于:2025-04-03 ⋅ 阅读:(15) ⋅ 点赞:(0)

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 splitter(filePath, number, progressBar);
                splitter.split();
        }
};

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++){
                        //...

                        if (m_progressBar != nullptr){
                                progressBar->setValue( (i + 1) / m_fileNumber);        //  更新进度条
                        }
                }
        }
};

违背了依赖倒置设计原则


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;
                        onProgress(progressValue);
                }
        }

protected:
        virtual void onProgress(float value){
                if (m_iprogress != nullptr){
                        m_iprogress->DoProgress(progressValue);        //  更新进度条
                }
        }
};

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);
        }
};

此时FileSplitter没有耦合任何一个界面类,就可以独立编译,在哪里都能支持进度通知。

但仍存在问题:不好支持多个通知。

要支持多个观察者,现在只支持一个观察者(MainForm或ProgressBar)。


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 {
        virtual void DoProgress(float value) {
                cout << "." <<;
        }
};

class FileSplitter{
        string m_filePath;
        int m_fileNumber;

        //ProgressBar*  m_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);
                }
        }

        void addIProgress(IProgress* iprogress){
                m_iprogressList.add(iprogress);
        }

        void removeIProgress(IProgress* iprogress){
                m_iprogressList.remove(iprogress);
        }

protected:
        virtual void onProgress(float value){
                List<IProgress*>::iterator it = m_iprogressList.begin();

                while(it != m_iprogressList.end() ){
                        (*it)->DoProgress(value);        //  更新进度条
                        it++;
                }
        }
};


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



Observer:IProgress        Update:DoProgress        Attach:addIProgress        Detach:removeIProgress        Notify:onProgress        ConcreteSubject:FileSplitter
ConcreteObserver:MainForm和ConsoleNotifier

Observer和Subject是稳定的。下面具体的是变化的。


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

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

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

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


网站公告

今日签到

点亮在社区的每一天
去签到