设计模式-观察者模式

1 观察者模式

  • 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
  • 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
  • 观察对象的状态发生变化时,通知给观察者。 观察者模式适用于根据对象状态进行相应处理的场景。

2 问题

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
// FileSplitter.cpp
class FileSplitter{
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar; // 不能应对变化,和具体某个平台的进度条绑死:例如UI进度条、无界面的Shell进度条等等

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++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
m_progressBar->setValue(progressValue);
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MainForm.cpp
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();
}
};

3 观察者v1

观察者模式
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
// FileSplitter.cpp
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
38
// MainForm.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};

// C++不推荐多继承,但是推荐的唯一一种多继承的形式是:一个主集成、其他都是接口或者抽象基类
// MainForm是观察者
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);
splitter.addIProgress(this); // 订阅通知
ConsoleNotifier cn; // 观察者二
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 << "."; // 无UI:打点
}
};

4 观察者v2(可执行)

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "Header.h"
#include <random>
#include <thread>

class NumGen;
class NumGenObserver {
public:
virtual ~NumGenObserver() {}
virtual void update(NumGen *numGen) = 0;
};

class NumGen {
public:
virtual ~NumGen() {}
void addObserve(NumGenObserver *observe) {
m_observeList.push_back(observe);
}
void deleteObserve(NumGenObserver *observe) {
m_observeList.erase(std::remove(m_observeList.begin(), m_observeList.end(), observe));
}
void notifyObverses() {
for (auto gen : m_observeList) {
gen->update(this);
}
}
virtual int getNumber() = 0;
virtual void execute() = 0;

private:
std::vector<NumGenObserver *> m_observeList;
};

class RandomNumGen : public NumGen {
public:
virtual int getNumber() override {
return m_number;
}
virtual void execute() override {
for (int i = 0; i < 20; ++i) {
m_number = m_dis(m_gen);
notifyObverses();
}
}

private:
std::random_device m_rd;
std::mt19937 m_gen{ m_rd() };
std::uniform_int_distribution<int> m_dis{ 0, 100 };
int m_number{ 0 };
};

class DigitObserver : public NumGenObserver {
public:
virtual void update(NumGen *numGen) override {
std::cout << "DigitObserver:";
std::cout << numGen->getNumber() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};

class GraphObserver : public NumGenObserver {
public:
virtual void update(NumGen *numGen) override {
auto num = numGen->getNumber();
std::cout << "GraphObserver:";
for (int i = 0; i < num; ++i) {
std::cout << "*";
}
std::cout << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};

int main() {
NumGen *gen = new RandomNumGen;
NumGenObserver *ob1 = new DigitObserver;
NumGenObserver *ob2 = new GraphObserver;
gen->addObserve(ob1);
gen->addObserve(ob2);
gen->execute();
delete gen;
delete ob1;
delete ob2;
return 0;
}

5 观察者v3(配合模板更通用的观察者模式)

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
template<typename ObserverType>
class Subject {
vector<ObserverType*> _list;
public:
// 订阅
void subscribe(ObserverType* obs) {
auto itor = std::find(_list.begin(), _list.end(), obs);
if (_list.end() == itor) {
_list.push_back(obs);
}
}

// 取消订阅
void unSubscribe(ObserverType* obs) {
// erase配合remove
_list.erase(std::remove(_list.begin(), _list.end(), obs));
}

// 发布
template<typename FuncType>
void publish(FuncType func) {
for (auto obs : _list) {
// 调用回调函数,将obs作为一个参数传入
func(obs);
}
}
};

// CatObserver接口 猫的观察者
class CatObserver {
public:
virtual void onMiaow() = 0;
virtual ~CatObserver() {}
};

// Tom继承自Subject,模板参数CatObserver
// 这样Tom就可以订阅、发布对应类型
class Tom : public Subject<CatObserver> {
public:
void miaow() {
cout << "喵" << endl;
// 这里CatObserver的成员函数,所以第一个参数需要this指针,这里悬置->对应publish的object
publish(std::bind(&CatObserver::onMiaow, std::placeholders::_1));
}
};

// Jerry继承自CatObserver,可以被订阅
class Jerry : public CatObserver {
public:
void onMiaow() override {
RunAway();
}
void RunAway() {
cout << "那只笨又猫来了,快跑!" << endl;
}
};

int main() {
Tom tom;
Jerry jerry1, jerry2, jerry3;

// 拿一堆jerry去订阅tom的 猫叫 事件
tom.subscribe(&jerry1);
tom.subscribe(&jerry2);
tom.subscribe(&jerry3);

tom.miaow();
}

输出结果:

1
2
3
4

那只笨又猫来了,快跑!
那只笨又猫来了,快跑!
那只笨又猫来了,快跑!

6 总结

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

7 参考