Skip to content

Latest commit

 

History

History
254 lines (198 loc) · 6.12 KB

03.观察者模式.md

File metadata and controls

254 lines (198 loc) · 6.12 KB

观察者模式 Observer

动机

软件构建过程中,需要为某些对象建立一种“通知依赖关系”--一个对象(目标对象)的状态发生变化,所有的依赖对象(观察者对象)都将得到通知

模式定义

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

代码

文件分割器

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

原始设计

需求:提供进度条进行展示

class FileSplitter{
    string m_filePath;
    int m_fileNumber;
+   ProgressBar* m_progressBar; // 通知控件

public:
-    FileSplitter(const string& filePath, int fileNumber) : 
-        m_filePath(filePath), m_fileNumber(fileNumber){ }
+    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){
+               m_progressBar->setValue((i+1) / m_fileNumber); // 更新进度条
+           }
        }
    }
};

// 界面
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);
+        FileSplitter splitter(filePath, number, progressBar);
        splitter.split();
    }
};

以上实现存在的问题:违反依赖倒置原则

  • FileSplitter 依赖 ProgressBar(实现细节)

重构设计

需求:提供进度条进行展示

仅支持一个观察者

+class IProgress{
+public:
+    virtual void DoProgress(float value) = 0;
+    virtual void ~IProgress(){}
+};

class FileSplitter{
    string m_filePath;
    int m_fileNumber;
+   IProgress* m_iprogress;   // 抽象通知机制

public:
-    FileSplitter(const string& filePath, int fileNumber) : 
-        m_filePath(filePath), m_fileNumber(fileNumber){ }
+    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:
+    void onProgress(float value){
+        if(m_iprogress != nullptr){
+            m_iprogress->DoProgress(value); // 更新进度条
+        }
+    }
};

// 界面
-class MainForm : public Form{
+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);
+        FileSplitter splitter(filePath, number, this);

        splitter.split();
    }

+    void DoProgress(float value){
+        progressBar->setValue(value);
+    }
};

支持多个观察者

+class IProgress{
+public:
+    virtual void DoProgress(float value) = 0;
+    virtual void ~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 addIProgress(IProgress* iprogress){
+        m_iprogressList.push_back(iprogress);
+   }
+    void removeIProgress(IProgress* iprogress){
+        m_iprogressList.pop_back(iprogress);
+    }

    void split(){
        //1. 读取大文件

        //2. 分批次向小文件中写入
        for(int i = 0; i < m_fileNumber; i++){
            //...

+           float progressValue = m_fileNumber;
+           progressValue = (i+1) / progressValue
+           onProgress(progressValue);               
        }
    }
+protected:
+    void onProgress(float value){
+        list<IProgress*>::iterator itor = m_iprogressList.begin();
+        while(itor != m_iprogressList.end()){
+            itor->DoProgress(value);
+            itor++;
+        }
+    }
};

// 界面
-class MainForm : public Form{
+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);
    }

+    void DoProgress(float value){
+        progressBar->setValue(value);
+    }
};

+class ConsoleNotifier : public IProgress{
+public:
+    virtual void DoProgress(float value){
+        cout << ".";
+    }
};

题目