0. 简介
个人认为观察者模式作为行为模式中非常重要的一个部分,我们日常使用的事件机制就是一个观察者模式,当事件发生,所有的事件接收者执行事件响应函数。在ROS中的subscribers
函数也是使用了类似的思想。它允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
1. 订阅者模式示意图
从ROS的Topic层面理解,拥有一些值得关注的状态的对象通常被称为目标, 由于它要将自身的状态改变通知给其他对象, 我们也将其称为发布者 (publisher)。 所有希望关注发布者状态变化的其他对象被称为订阅者 (subscribers)。
观察者模式建议你为发布者类添加订阅机制, 让每个对象都能订阅或取消订阅发布者事件流。该机制包括:
- 一个用于存储订阅者对象引用的列表成员变量;
- 几个用于添加或删除该列表中订阅者的公有方法。
从图中我们可以看到,通过发布和订阅的机制可以有效的实现触发的方式,相比于传统的主动触发观察者模式,使用回调函数去完成观察者的时间监听触发是一个更好的选择。
2. 示例代码
2.1 普通的触发
这部分的内容没有使用回调函数去完成触发,通过调用来实现函数的使用
#include <iostream>
#include <set>
using namespace std;
//观察者接口,包括事件响应函数
class Observer
{
public :
virtual void update(int n)=0;
};
//目标类,负责触发事件
class Subject
{
public:
Subject(){};
//添加事件监听
void addObserver(Observer *observer)
{
observerSet.insert(observer);
}
//删除事件监听
void removeObserver(Observer *observer)
{
observerSet.erase(observer);
}
//触发事件
void notify(int n)
{
set<Observer *>::iterator iter;
for(iter = observerSet.begin() ; iter != observerSet.end() ; ++iter)
{
(*iter)->update(n);
// iter. ->update();
}
}
private:
set<Observer *> observerSet;
};
class Observer1 :public Observer
{
public:
void update(int n)
{
cout<<"观察者1事件响应函数,接受参数:"<<n<<endl;
}
};
class Observer2 :public Observer
{
public:
void update(int n)
{
cout<<"观察者2事件响应函数,接受参数:"<<n<<endl;
}
};
main()
{
Subject subject;
Observer * observer1 = new Observer1();
Observer * observer2 = new Observer2();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify(4);
subject.notify(1);
}
2.2 回调函数触发
这部分通过模板类来实现函数的调用,通过operator()
来调用invoke函数实现回调触发。
/* 实现事件的信号类 */
template <typename... TFuncArgs>
class Signal {
public:
using Callback = std::function<void(TFuncArgs...)>;
/* 连接类, 一旦结束,Disconnect()将被自动调用。
*/
class SignalConnection {
private:
friend class Signal;
/* 只允许类Signal创建连接 */
SignalConnection(Signal& signal, int id) noexcept:id(id), signal(signal) {}
public:
/* C++11 成员函数声明新特性: = delete,从逻辑语义上禁止了对于SignalConnection类拷贝构造函数和赋值运算符*/
SignalConnection(const SignalConnection& copy) = delete;
/* 如果没有复制构造函数,则不能返回SignalConnection,除非提供一个move构造函数。 */
SignalConnection(SignalConnection&& toMove) noexcept : id(toMove.id), signal(toMove.signal),disconnected(toMove.disconnected) {}
~SignalConnection() {
Disconnect();
}
int id;
Signal& signal;
bool disconnected = false;
void Disconnect() {
if (disconnected)
return;
disconnected = true;
signal.Disconnect(*this);
}
};
/* 返回连接对象,连接对象一旦超出作用域将自动断开连接 */
SignalConnection Connect(Callback callback) {
callbacks.push_back(std::pair<int, Callback>(idRoller++, callback));
return SignalConnection(*this, idRoller - 1);
}
void Invoke(TFuncArgs... args) {
for (auto& con : callbacks) {
(con.second)(args...);
}
}
void operator()(TFuncArgs... args) {
Invoke(args...);
}
private:
/* ID Counter.
使用id查找连接对应的回调函数,因为std::function的操作符==并不像想象的那样工作。*/
int idRoller = 0;
std::list<std::pair<int, Callback>> callbacks;
void Disconnect(SignalConnection& t) {
callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(), [&](auto& pCallback) {
return pCallback.first == t.id;
}), callbacks.end());
}
};
void TestFunc1(int t) {
cout << "Func1" << endl;
}
void TestFunc2(int t,string someArg) {
cout << "Func2" << endl;
}
int main(){
using TestDelegate = Signal<int>;
TestDelegate testEvent;
TestDelegate::SignalConnection testCon1 = testEvent.Connect(&TestFunc1);
TestDelegate::SignalConnection testCon2 = testEvent.Connect(std::bind(TestFunc2 ,std::placeholders::_1, "arg")); //用bind去绑定参数
auto testCon3 = testEvent.Connect(&TestFunc1); //用auto简化声明
testEvent(1);
...
3. 参考链接
https://blog.csdn.net/tm8426/article/details/73472496
https://refactoringguru.cn/design-patterns/observer/cpp/example#lang-features
https://www.cnblogs.com/hebaichuanyeah/p/6091694.html
https://blog.csdn.net/liukang325/article/details/44461449
https://www.cnblogs.com/yangrouchuan/p/7817773.html
评论(0)
您还未登录,请登录后发表或查看评论