IE盒子

搜索
查看: 123|回复: 2

李建忠设计模式C++版(三)

[复制链接]

3

主题

6

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-12-9 19:47:31 | 显示全部楼层 |阅读模式
三、观察者模式


  • 组件协作模式(晚绑定)——框架与应用程序划分


  • 模板方法
  • 策略模式
  • 观察者模式
2. 动机与背景

  • 文件分割器。大文件需要分成若干个小文件,这其中需要进行进度展示(或者其他的类似场景),但是需要的进度展示不是一个,而是多个。
3. 没有使用设计模式的代码
#include <iostream>
using namespace std;

class FileSplitter {
private:
    string m_filePath;
    int m_fileNumber;
    ProgressBar* m_progressBar;

public:
    FileSplitter(string& filePath, int fileNumber, ProgressBar* progressBar):
        m_filePath(filePath),
        m_fileNumber(fileNumber),
        m_progressBar(progressBar) {
    }

    void split() {
        for (int i = 0; i < m_fileNumber; ++i) {
            m_progressBar->setValue();
        }
    }
};

// 主逻辑
class Form {};

class TextBox {
public:
    string getText() {
        return "";
    }
};

class ProgressBar {
public:
    void setValue() {}
};

class MainForm: Form {
private:
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;

public:
    void buttonClick() {
        string filePath = txtFilePath->getText();
        int fileNumber = atoi(txtFileNumber->getText().c_str());

        FileSplitter* splitter = new FileSplitter(filePath, fileNumber, progressBar);
        splitter->split();
    }
};
4. 存在的问题

  • 观察者会变化,需要更改目标的通知实现方式,对于观察者增减的需求不满足开闭原则;通知的具体实现在目标处完成,没有独立抽象,不符合DIP原则。
5. 解决方法

  • 使用观察者模式:定义对象间的一对多的关系,以便一个对象发生变化时,能够及时通知所依赖的其他对象(观察者),方便添加或删除观察者,并且符合DIP原则。
6. 使用设计模式的代码
#include <iostream>
#include <vector>
using namespace std;

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

class FileSplitter {
private:
    string m_filePath;
    int m_fileNumber;
    // 抽象通知机制
    IProgress* m_iprogress;
    // 多个观察者使用容器保存
    vector<IProgress*> m_iprogressVector;

public:
    FileSplitter(string& filePath, int fileNumber, IProgress* iprogress):
        m_filePath(filePath),
        m_fileNumber(fileNumber),
        m_iprogress(iprogress) {
    }

    void split() {
        for (int i = 0; i < m_fileNumber; ++i) {
            m_iprogress->DoProgress(i);
            // 多个观察者使用容器保存
        }
    }

    // 多个观察者
    void add() {}

    void remove() {}
};

// 主逻辑
class Form {};

class TextBox {
public:
    string getText() {
        return "";
    }
};

class ProgressBar {
public:
    void setValue() {}
};

class MainForm: public Form, public IProgress {
private:
    TextBox* txtFilePath;
    TextBox* txtFileNumber;
    ProgressBar* progressBar;

public:
    void buttonClick() {
        string filePath = txtFilePath->getText();
        int fileNumber = atoi(txtFileNumber->getText().c_str());

        // this比较重要
        FileSplitter* splitter = new FileSplitter(filePath, fileNumber, this);

        splitter->split();
    }

    // 不论有多少个观察者,都可以实现自己的通知机制
    virtual void Doprogress(float value) {
        progressBar->setValue();
    }
};
7. 适用场景

  • 通知事件时,被通知的对象(观察者)经常变化,但不需要频繁改变目标的通知方式。
8. 总结

  • 可以独立改变目标与观察者(松耦合)
  • 目标发送通知时,无需指定观察者,只管通知机制,而通知自动传播
  • 观察者自己决定是否订阅通知
9. 依赖

  • 编译时依赖,在编译A之前必须完成B的编译,我们就说A依赖于B
  • 运行时依赖
回复

使用道具 举报

1

主题

5

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2022-12-9 19:48:26 | 显示全部楼层
弱弱问下李建忠是哪位大牛?[可怜]
回复

使用道具 举报

1

主题

3

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2022-12-9 19:49:23 | 显示全部楼层
有一个叫Boolan教育的主讲[捂脸]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表