0%

设计模式之观察者模式

设计模式基础(3):观察者模式

介绍观察者模式的C++示例代码、面向的问题、图解观察者模式核心思想

面向的需求

在一些软件的需求中需要多个对象之间建立一种“通知依赖关系”。即一个对象(被观察者)有变化,所有的依赖对象(观察者对象)都将得到通知。也可以说是通知-订阅模式。被观察者和观察者之间如果连接的过于紧密(一个变化,全体都需要变化)。那么就是应对变化的能力较差。

可以采用面向对象的技术,弱化具体联系,抽象其关系,利用抽象类来松耦合观察者与被观察者。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//抽象的观察者类
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~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 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.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++;
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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 {
public:
virtual void DoProgress(float value){
cout << ".";
}
};

分析代码及思想

程序的功能是:在分割文件时,显示文件分割的进度。分割函数(被观察者)中这个进度(事件)需要通知到界面和控制台(观察者)。

  • 紧耦合的做法是,直接传参一个显示控件到分割函数中,得到进度并显示。但是此时,分割函数和界面均需要变化。
  • 松耦合则是先抽象一个IProgress类,之后在分割函数中维护一个IProgress集合,并且动态维护。当发生事件时,逐个通知集合中的抽象类IProgress,抽象类中设计一个虚响应函数,每个需要接收信号的类,继承该抽象类并复写(override)这个响应函数,实现自身面对信号的响应。主要注意的是,将自身加入到动态维护的集合中。

关键点总结

  • 使用面向对象的抽象,Observer模式使得目标者和观察者达到松耦合,自身改变,不会影响对方。
  • 目标发送通知时,无需指定观察者,通知会自动传播。
  • 观察者自己决定是否需要订阅通知,目标对象不必知道。

图解观察者模式

观察者模式