观察者模式(Observer Pattern
)解决的对象间的一对多的关系,即一个被观察者与多个观察者。最直接的例子就是公众号(Subject
)及订阅者(Observer
)。
对于观察者模式、发布-订阅模式/事件监听模式,为只是相同技术实现基础上的不同演化,属于同一类设计模式。
观察者模式1.0¶
涉及两个角色:
- 被观察者: Subject/Observable/Publisher, 会有状态/数据变化的对象。
- 观察者: Observer/Subscriber,关心
被观测对象
的变化。
最基础的实现需要定义 2 个基类 Subject
和 Observer
。
C++
实现
观察者¶
先定义接口类 class Observer
,该类没有默认实现,子类必须实现 update()
接口。
1
2
3
4
5
6
|
// Observer.h
class Observer {
public:
virtual void update(string msg) = 0; // abstract virtual
};
|
被观察对象¶
基类 Subject
,它需要知道有那些观察者,在需要通知的时候遍历观察者列表发送通知。
有自己的实现,定义子类时不需要复写基类的方法,直接使用即可。
1
2
3
4
5
6
7
8
9
10
11
|
// Subject.h
class Subject {
public:
virtual void addObserver(Observer * observer);
virtual void removeObserver(Observer * observer);
virtual void notify(string msg);
private:
std::set<Observer*> observers;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// Subjet.cpp
void Subject::addObserver(Observer* observer) {
observers.insert(observer);
}
void Subject::removeObserver(Observer* observer) {
observers.erase(observer);
}
void Subject::notify(string msg) {
for (auto obs: observers) {
obs->update(msg);
}
}
|
以公众号和订阅者为例。
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
|
// 公众号
class Publisher: public Subject {
public:
void addArticle(const string& article) {
articles.push_back(article);
notify(article);
}
private:
vector<string> articles;
};
// 订阅者
class Subscriber: public Observer {
public:
Subscriber(string name): name(name) {}
virtual void update(string article) override {
readArticle(article);
}
void readArticle(string article) const {
cout << name << " is reading article : " << article << endl;
}
private:
string name;
};
int main() {
Publisher publisher;
Subscriber sub1("Tom");
publisher.addObserver(&sub1);
publisher.addArticle("Article 1");
cout << endl;
Subscriber sub2("Jerry");
publisher.addObserver(&sub2);
publisher.addArticle("Article 2");
return 0;
}
|
执行结果:
1
2
3
4
|
Tom is reading article : Article 1
Tom is reading article : Article 2
Jerry is reading article : Article 2
|
观察者模式2.0¶
上面的Observer
提供了一个带有一个参数的update
接口: virtual void update(string msg)
。
虽然在应用中可以通过传递不同参数来区分不同的通知,比如:update("全部订阅者可见更新")
,update("VIP订阅者可见更新")
,但对于复杂一点的需要多参数传递的场景就不适用了。
那么可以通过引入Event
类型,以更灵活的定义不同的、复杂的通知类型。
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
|
// Event 接口定义
enum EventType {
ARTICLE_UPDATED,
VIP_ARTICLE_UPDATED
};
class Event {
public:
virtual EventType type() = 0;
virtual ~Event() {}
};
// 具体的 Event 类型
class ArticleUpdatedEvent: public Event {
public:
virtual EventType type() override { return ARTICLE_UPDATED; }
string getArticle() const { return article; }
void setArticle(const string& article) { this->article = article; }
private:
string article;
};
class VipArticleUpdatedEvent: public Event {
public:
virtual EventType type() override { return VIP_ARTICLE_UPDATED; }
string getArticle() const { return article; }
void setArticle(const string& article) { this->article = article; }
int getReward() const { return reward; }
void setReward(int reward) { this->reward = reward; }
private:
string article;
int reward;
};
|
将Observer
更新为:
1
2
3
4
5
6
7
|
// Observer.h
class Observer {
public:
// virtual void update(string msg) = 0; // abstract virtual
virtual void update(shared_pointer<Event> event) = 0; // abstract virtual
};
|
Subject
更新为:
1
2
3
4
5
6
7
8
9
|
// Subject.h
class Subject {
public:
...
// virtual void notify(string msg);
virtual void notify(shared_pointer<Event> event);
...
};
|
应用到公众号——订阅者
上:
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
|
// 公众号
class Publisher: public Subject {
public:
void addArticle(const string& article) {
auto e = make_shared<ArticleUpdatedEvent>();
e->setArticle(article);
notify(e);
}
void addVipArticleWithReward(const string& article, int reward) {
auto e = make_shared<VipArticleUpdatedEvent>();
e->setArticle(article);
e->setReward(reward);
notify(e);
}
};
// 订阅者
class Subscriber: public Observer {
public:
Subscriber(string name): name(name) {}
virtual void update(shared_ptr<Event> event) override {
switch (event->type()) {
case EventType::ARTICLE_UPDATED: {
auto e = dynamic_cast<ArticleUpdatedEvent*>(event.get());
if (e != nullptr) {
string article = e->getArticle();
readArticle(article);
}
break;
}
case EventType::VIP_ARTICLE_UPDATED: {
auto e = dynamic_cast<VipArticleUpdatedEvent*>(event.get());
if (e != nullptr) {
string vipArticle = e->getArticle();
readArticle(vipArticle);
int reward = e->getReward();
earnReward(reward);
}
break;
}
default:
break;
}
}
void readArticle(string article) const {
cout << name << " is reading article : " << article << endl;
}
void earnReward(int reward) const {
cout << name << " earned reward : " << reward << endl;
}
private:
string name;
};
int main() {
Publisher publisher;
Subscriber sub1("Tom");
publisher.addObserver(&sub1);
publisher.addArticle("Article 1");
cout << endl;
Subscriber sub2("Jerry");
publisher.addObserver(&sub2);
publisher.addVipArticleWithReward("Article 2", 5);
return 0;
}
|
执行结果:
Tom is reading article : Article 1
Tom is reading article : Article 2
Tom earned reward : 5
Jerry is reading article : Article 2
Jerry earned reward : 5
通过继承Event
,这个版本支持灵活的扩展通知类型,对传递的参数个数和类型没有任何限制。
目前为止,被订阅对象发送 notify
时对所有的订阅者都是一视同仁的,也就是说每个订阅者接收到的消息是一样的。
对于上面公众号的例子,如果想实现对 VIP 订阅者和普通订阅者推送不同的通知,那就可以通过如下的方式。
观察者模式3.0¶
更改 Subject
的订阅和通知方法:
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
|
// Subject.h
class Subject {
public:
virtual void addObserver(EventType type, Observer * observer);
virtual void removeObserver(EventType type, Observer * observer);
virtual void notify(shared_ptr<Event> event);
private:
std::map<EventType, std::set<Observer*> > observers;
};
// Subjet.cpp
void Subject::addObserver(EventType type, Observer* observer) {
observers[type].insert(observer);
}
void Subject::removeObserver(EventType type, Observer* observer) {
observers[type].erase(observer);
}
void Subject::notify(shared_ptr<Event> event) {
for (auto obs: observers[event->type()]) {
obs->update(event);
}
}
|
修改main
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
int main() {
Publisher publisher;
Subscriber sub1("Tom");
publisher.addObserver(EventType::ARTICLE_UPDATED, &sub1);
publisher.addArticle("Article 1");
cout << endl;
Subscriber sub2("Jerry");
publisher.addObserver(EventType::VIP_ARTICLE_UPDATED, &sub2);
publisher.addVipArticleWithReward("VIP Article 2", 5);
return 0;
}
|
执行结果:
Tom is reading article : Article 1
Jerry is reading article : VIP Article 2
Jerry earned reward : 5
此版本进化到了一个更为特殊的观察者模式———发布订阅者模式,或者叫事件订阅模式。
Observer Pattern v.s. Publish-Subscribe Pattern¶
其主要区别是观察者模式涉及两种角色 Subject
和 Observer
,而发布订阅模式中间多了一个Event
处理,有的称之为Broker
或Channel
:

完整代码¶