基于模板实现的观察者模式C++

这个设计模式的功能就是实现订阅与发布的功能,一旦有消息更新,即可将最新的消息同步给所有的订阅者,无需订阅者主动来询问,如果订阅者无需这些消息了,只需要取消订阅即可,后续有新的消息就不会再次打扰。

适用于对于相同的数据需要表现为不同的行为的功能,比如,得知爸爸要回来了,儿子就会立马停止玩游戏,妈妈立马开始准备晚餐。也有可能是很多人都想订阅这些数据,比如求职者先将要找工作的需求交给猎头,猎头那里有很多这样的需求,一旦有公司有需求,猎头那里就会立马通知所有的要找这份工作的求职者,让他们分别去面试,公司选择优秀的人才入职。

生活中有很多类似的场景,都可以用观察者模式来解释,所以设计模式的灵感也来源于生活。另外一方面,也实现了逻辑与应用的分离,从上面的例子也可以看出,底层数据只有一份,但是可以用在几个不同的场景,如果新的场景,也就相当于多了一个订阅者而已。

基于模板实现的观察者模式

网上面有很多实现了的观察者模式,用的都是普通类,但由于模型应用场景,比如订阅的消息类型各式各样,有string、int或者自定义class,那就需要改用模板类了。

由普通类改为模板类确实费了一点功夫,主要是对于模板类的不够熟悉,容易踩很多坑,比如,继承模板类的子类中,如果父类的成员变量是protect的,那子类是无法直接访问的,需要加this指针或者父类的作用域限定符。如果在成员函数里面使用this指针进行共享,注意不能直接使用std::shared_ptr(this),这样会导致单个指针被多个共享类使用的,最后释放的时候就会有二次delete的问题,推荐使用enable_shared_from_this,后面代码有体现,正确的用法。

1
2
3
4
5
6
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
std::shared_ptr<MyClass> getSharedPtr() {
return shared_from_this();
}
};

还有,对于模板类的实现需要和声明放在同一个文件中,否则链接的时候就会找不到定义,这一点和声明普通类不一样的,需要特别注意。

另外,订阅者类中在调用观察者对象时,应该用观察者的抽象类,同时,观察者类在调用订阅者类时,也应该使用订阅者的抽象类,否则会相会包含,编译就会报错,而把调用类抽象出来就避免了这个问题,再实际实例化对象时再new具体对象就行,可以看作时多态的一种体现,也可以说是面向接口编程,不要面向具体类编程。语言描述不太清楚,类图参考入下,使用draw.io绘制。

可以看出,具体类之间是没有相互关联的,有什么事请找抽象类,这样就一目了然了,后面再扩展别的观察者类的时候,只需要按照抽象类的接口实现的,就都可以进行订阅消息,不需要更改原来的类,所以是对修改关闭,对扩展开放,符合设计模式基本原则。

代码实现

参考了别人的代码,实现了两个版本,上面的uml图为第二个版本。

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <iostream>
#include <string>
#include <list>
#include <memory>

using namespace std;

template <typename T>
class Subject;

// 抽象观察者
template <typename T>
class Observer
{
public:
using SubjectPtr = std::shared_ptr<Subject<T>>;
Observer(string name, SubjectPtr sub)
{
this->name = name;
this->sub = sub;
}
virtual void update() = 0;
protected:
string name;
SubjectPtr sub;
};

// 具体的观察者,看股票的
template <typename T>
class StockObserver : public Observer<T>
{
public:
using SubjectPtr = std::shared_ptr<Subject<T>>;
StockObserver(string name, SubjectPtr sub) : Observer<T>(name, sub)
{
}
void update();
};

// 具体的观察者,看NBA的
template <typename T>
class NBAObserver : public Observer<T>
{
public:
using SubjectPtr = std::shared_ptr<Subject<T>>;
NBAObserver(string name, SubjectPtr sub) : Observer<T>(name, sub)
{
}
void update();
};

//抽象通知者
template <typename T>
class Subject
{
public:
using ObserverPtr = std::shared_ptr<Observer<T>>;
virtual void attach(ObserverPtr &) = 0;
virtual void detach(ObserverPtr &) = 0;
virtual void notify() = 0;

T action;
protected:
list<ObserverPtr> observers;
};

//具体通知者,秘书
template <typename T>
class Secretary : public Subject<T>
{
public:
using ObserverPtr = std::shared_ptr<Observer<T>>;
void attach(ObserverPtr &observer) override
{
this->observers.push_back(observer);
}
void detach(ObserverPtr &observer) override
{
auto iter = this->observers.begin();
while (iter != this->observers.end())
{
if ((*iter) == observer)
{
this->observers.erase(iter);
}
++iter;
}
}
void notify() override
{
auto iter = this->observers.begin();
while (iter != Subject<T>::observers.end())
{
(*iter)->update();
++iter;
}
}
};

template <typename T>
void StockObserver<T>::update()
{
cout << this->name << " 收到消息:" << this->sub->action << endl;
if (this->sub->action == "梁所长来了!")
{
cout << "我马上关闭股票,装做很认真工作的样子!" << endl;
}
}

template <typename T>
void NBAObserver<T>::update()
{
cout << this->name << " 收到消息:" << this->sub->action << endl;
if (this->sub->action == "梁所长来了!")
{
cout << "我马上关闭NBA,装做很认真工作的样子!" << endl;
}
}

int main()
{
// 创建观察者
std::shared_ptr<Subject<string>> sub = std::make_shared<Secretary<string>>();
// 被观察的对象
std::shared_ptr<Observer<string>> xs = std::make_shared<NBAObserver<string>>("xiaoshuai", sub);
std::shared_ptr<Observer<string>> zy = std::make_shared<NBAObserver<string>>("zouyue", sub);
std::shared_ptr<Observer<string>> lm = std::make_shared<StockObserver<string>>("limin", sub);

sub->attach(xs);
sub->attach(zy);
sub->attach(lm);

sub->action = "去吃饭了!";
sub->notify();
cout << endl;
sub->action = "梁所长来了!";
sub->notify();
return 0;
}

第二个版本是将类分文件整理了,另外在观察者中可以进行订阅和取消订阅,比如观察者update出问题的时候,可以选择自动取消订阅。

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// IObservable.hpp
#pragma once

#include "IObserver.hpp"

template<typename T>
class IObservable
{
public:
using IObserverPtr = std::shared_ptr<IObserver<T>>;
virtual void NotifyObservers(T data) = 0;
virtual void AddObserver(const IObserverPtr &obs) = 0;
virtual void RemoveObserver(const IObserverPtr &obs) = 0;
};

// Observable.hpp
#pragma once

#include <list>
#include <string>
#include <memory>
#include <iostream>

#include "IObserver.hpp"
#include "IObservable.hpp"

template<class T>
class Observable : public IObservable<T>
{
public:
using IObserverPtr = std::shared_ptr<IObserver<T>>;
void NotifyObservers(T data) override
{
std::list<IObserverPtr> m_observers_tmp = m_observers;
for (auto o : m_observers_tmp)
{
o->Notify(data);
}
}

void AddObserver(const IObserverPtr &obs) override
{
m_observers.push_back(obs);
}

void RemoveObserver(const IObserverPtr &obs) override
{
m_observers.remove(obs);
}

private:
std::list<IObserverPtr> m_observers;
};

// IObserver.hpp
#pragma once
#include <memory>
#include <iostream>

template <class T>
class IObserver
{
public:
virtual ~IObserver() {};
virtual void Notify(T data) = 0;
protected:
};

// Observer.hpp
#pragma once

#include <iostream>
#include <memory>

#include "IObserver.hpp"
#include "Observable.hpp"

template <class T>
class CurrentConditionDisplay : public IObserver<T>, public std::enable_shared_from_this<CurrentConditionDisplay<T>>
{
using IObservablePtr = std::shared_ptr<IObservable<T>>;
public:
CurrentConditionDisplay(IObservablePtr &sub) : subject(sub)
{
}
void Notify(T data) override
{
std::cout << "CurrentConditionDisplay: " << data << std::endl;
subject->RemoveObserver(this->shared_from_this());
}
protected:
IObservablePtr &subject;
};

template <class T>
class GeneralDisplay : public IObserver<T>
{
public:
GeneralDisplay() = default;
void Notify(T data) override
{
std::cout << "GeneralDisplay: " << data << std::endl;
}
protected:
};

// main.cpp
#include "Observable.hpp"
#include "Observer.hpp"

#include <iostream>
#include <memory>

struct data
{
int a;
std::string b;
//重载输出流
friend std::ostream& operator<<(std::ostream&out, const data &p)
{
out << p.a << " " << p.b << std::endl;
return out;
}
};

int main()
{
{
std::shared_ptr<IObservable<int>> subject = std::make_shared<Observable<int>>();
std::shared_ptr<IObserver<int>> observer = std::make_shared<CurrentConditionDisplay<int>>(subject);
subject->AddObserver(observer);
std::shared_ptr<IObserver<int>> observer2 = std::make_shared<GeneralDisplay<int>>();
subject->AddObserver(observer2);
subject->NotifyObservers(12345);
subject->NotifyObservers(67890);
}
{
std::shared_ptr<IObservable<std::string>> subject = std::make_shared<Observable<std::string>>();
std::shared_ptr<IObserver<std::string>> observer = std::make_shared<CurrentConditionDisplay<std::string>>(subject);
subject->AddObserver(observer);
std::shared_ptr<IObserver<std::string>> observer2 = std::make_shared<GeneralDisplay<std::string>>();
subject->AddObserver(observer2);
subject->NotifyObservers("hello");
subject->NotifyObservers("world");
}
{
struct data dt = {1, "abc"};
std::shared_ptr<IObservable<data>> subject = std::make_shared<Observable<data>>();
std::shared_ptr<IObserver<data>> observer = std::make_shared<CurrentConditionDisplay<data>>(subject);
subject->AddObserver(observer);
subject->NotifyObservers(dt);
}
}
nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!