设计模式

观察者模式(Observer / Event)是组件协作模式的一种。

动机

  • 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
  • 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

代码示例:文件分割器

 1 class FileSplitter
 2 {
 3     string m_filePath;
 4     int m_fileNumber;
 5     ProgressBar* m_progressBar;  //通知控件(实现细节),产生了编译时依赖
 6 
 7 public:
 8     FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
 9         m_filePath(filePath), 
10         m_fileNumber(fileNumber),
11         m_progressBar(progressBar){
12 
13     }
14 
15     void split(){
16 
17         //1.读取大文件
18 
19         //2.分批次向小文件中写入
20         for (int i = 0; i < m_fileNumber; i++){
21             //...
22             float progressValue = m_fileNumber;
23             progressValue = (i + 1) / progressValue;  //更新进度条
24             m_progressBar->setValue(progressValue);
25         }
26 
27     }
28 };
FileSplitter1.cpp
 1 class MainForm : public Form  //MainForm主窗口界面
 2 {
 3     TextBox* txtFilePath;    //要分割的文件的路径
 4     TextBox* txtFileNumber;  //要分割成子文件的个数
 5     ProgressBar* progressBar;//分割过程的进度条
 6 
 7 public:
 8     void Button1_Click(){
 9 
10         string filePath = txtFilePath->getText();
11         int number = atoi(txtFileNumber->getText().c_str());
12         //实例化一个文件分割器对象,并初始化
13         FileSplitter splitter(filePath, number, progressBar);
14 
15         splitter.split();
16 
17     }
18 };
MainForm1.cpp

 由于有时文件比较大,新增需求:需要看到文件分割的进度,就在MainForm添加一个子控件processBar

违背了“依赖倒置原则”

 在类FileSplitter中,新增的通知控件m_processBar就是实现细节,当需求变更时,实现细节也会变(可能会以百分比显示,省略号显示等)。因此需求变化时,就产生了编译时依赖。

如何重构以上代码?

 1 class IProgress{
 2 public:
 3     virtual void DoProgress(float value)=0;
 4     virtual ~IProgress(){}
 5 };
 6 
 7 
 8 class FileSplitter
 9 {
10     string m_filePath;
11     int m_fileNumber;
12 
13     List<IProgress*>  m_iprogressList; // 抽象通知机制,支持多个观察者
14     
15 public:
16     FileSplitter(const string& filePath, int fileNumber) :
17         m_filePath(filePath), 
18         m_fileNumber(fileNumber){
19 
20     }
21 
22 
23     void split(){
24 
25         //1.读取大文件
26 
27         //2.分批次向小文件中写入
28         for (int i = 0; i < m_fileNumber; i++){
29             //...
30 
31             float progressValue = m_fileNumber;  //
32             progressValue = (i + 1) / progressValue;
33             onProgress(progressValue);//发送通知
34         }
35 
36     }
37 
38 
39     void addIProgress(IProgress* iprogress){
40         m_iprogressList.push_back(iprogress);
41     }
42 
43     void removeIProgress(IProgress* iprogress){
44         m_iprogressList.remove(iprogress);
45     }
46 
47 
48 protected:
49     virtual void onProgress(float value){
50         
51         List<IProgress*>::iterator itor=m_iprogressList.begin();
52 
53         while (itor != m_iprogressList.end() )
54             (*itor)->DoProgress(value); //更新进度条
55             itor++;
56         }
57     }
58 };
FileSplitter2.cpp
 1 //观察者——控件窗口
 2 class MainForm : public Form, public IProgress
 3 {
 4     TextBox* txtFilePath;
 5     TextBox* txtFileNumber;
 6 
 7     ProgressBar* progressBar;
 8 
 9 public:
10     void Button1_Click(){
11 
12         string filePath = txtFilePath->getText();
13         int number = atoi(txtFileNumber->getText().c_str());
14 
15         ConsoleNotifier cn;
16 
17         FileSplitter splitter(filePath, number);
18 
19         splitter.addIProgress(this); //订阅通知
20         splitter.addIProgress(&cn); //订阅通知
21 
22         splitter.split();
23 
24         splitter.removeIProgress(this);
25 
26     }
27 
28     virtual void DoProgress(float value){
29         progressBar->setValue(value);
30     }
31 };
32 
33 //观察者——控制台
34 class ConsoleNotifier : public IProgress {
35 public:
36     virtual void DoProgress(float value){
37         cout << ".";
38     }
39 };
MainForm2.cpp

实现细节应依赖于抽象。

progressBar是一个通知控件(实现细节), 本质为通知,故新建一个IProgress类表达一个抽象的通知机制。

让观察者MainForm继承IProgress,

不推荐多继承,但有一种情况: 继承了一个主的继承类,其他都是接口类或抽象基类。

在MainForm中,实现DoProgress方法,即具体细节。FileSpliter不再像之前的代码依赖于界面类,与之解耦合。

模式定义

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

 类图

Observer -- IProgress     

  Update() -- doProgress

Subject (可将以下三方法放在基类Subject中实现,让FileSpliter继承此基类)

  Attach(Observer )  -- addIProgress()  

  Detach(Observer ) -- removeIProgress()

  Notify()  --  onProgress()

ConcreteSubject -- FileSpliter(主体对象)

ConcreteObserver --  具体的观察者(ConsoleNotifier /  MainForm)

要点总结

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