我试图创建一个观察者模式,主体通知观察者不同的通知。
通常在观察者模式实现中,您只能看到一个名为notify
的方法,它通知观察者发生了某些事情并且有一种反转,其中观察者握住主体的指针并在询问主体时向主体询问它被通知了。
我实现这一点有点不同,主题附加观察者并通知他们所有人,而不需要将观察者的指针保持在观察者之内。例如:
#include <iostream>
#include <vector>
class ObserverEvents
{
public:
virtual addSomethingOne(int) = 0;
virtual addSomethingTwo(float) = 0;
};
class Observer : public ObserverEvents
{
public:
Observer();
~Observer();
virtual addSomethingOne(int) {}
virtual addSomethingTwo(float) {}
};
class Subject : public ObserverEvents
{
public:
Subject() {}
~Subject()
{
for (int i = 0; i < observers.size(); ++i)
{
delete observers[i];
}
}
void attach(Observer * observer)
{
observers.push_back(observer);
}
virtual addSomethingOne(int something)
{
for (int i = 0; i < observers.size(); ++i)
{
observers[i].addSomethingOne(something);
}
}
virtual addSomethingTwo(float something)
{
for (int i = 0; i < observers.size(); ++i)
{
observers[i].addSomethingTwo(something);
}
}
private:
std::vector<Observer *> observers;
};
class FooObserver : public Observer
{
public:
BarObserver() {}
~BarObserver() {}
addSomethingOne(int something)
{
// do something with something
}
addSomethingTwo(float something)
{
// do something with something
}
};
class BarObserver : public Observer
{
public:
BizObserver() {}
~BizObserver() {}
addSomethingOne(int something)
{
// do something with something
}
addSomethingTwo(float something)
{
// do something with something
}
};
int main(int argc, char const * argv[])
{
Subject subject;
subject.attach(new FooObserver());
subject.attach(new BarObserver());
return 0;
}
我唯一关心的是,如果我没有像开放式和封闭式等类似的设计原则那样,而且如果我需要添加新的通知,我需要在所有其他类。 (这很痛苦 - 想象10个观察者甚至更多)。
我在考虑让它变得与众不同,只创建一个界面然后我可以继承它创建其他通知但是存在问题,观察者如何确定每种不同类型的通知是什么?
示例:
#include <iostream>
#include <vector>
class Notifier
{
public:
Notifier() {}
~Notifier() {}
virtual int getInt() const = 0;
};
class FooNotifier
{
public:
FooNotifier() {}
~FooNotifier() {}
int getInt() const
{
return 10;
}
};
class BarNotifier
{
public:
BarNotifier() {}
~BarNotifier() {}
int getInt() const
{
return 50;
}
};
class Observer : public ObserverEvents
{
public:
Observer();
~Observer();
virtual receive(Notifier *) = 0;
};
class Subject : public ObserverEvents
{
public:
Subject() {}
~Subject()
{
for (int i = 0; i < observers.size(); ++i)
{
delete observers[i];
}
}
void attach(Observer * observer)
{
observers.push_back(observer);
}
virtual notify(Notifier * notification)
{
for (int i = 0; i < observers.size(); ++i)
{
observers[i].receive(notification);
}
}
private:
std::vector<Observer *> observers;
};
class FooObserver : public Observer
{
public:
BarObserver() {}
~BarObserver() {}
receive(Notifier * notification)
{
// ...
}
};
class BarObserver : public Observer
{
public:
BizObserver() {}
~BizObserver() {}
receive(Notifier * notification)
{
// ...
}
};
int main(int argc, char const * argv[])
{
Subject subject;
subject.attach(new FooObserver());
subject.attach(new BarObserver());
subject.notify(new FooNotifier());
subject.notify(new BarNotifier());
return 0;
}
实现只是一个例子,我知道我可以使用智能指针,删除原始指针并做得更好,但它只是一个实现的例子。
这种新方法的问题在于,我需要知道Notifier
的API是否可以在观察者中使用它来调用getInt
。
我该怎么做呢,这样做的最佳方式是什么?如何向观察者发送不同的通知?
答案 0 :(得分:2)
template<class C> class event {
C list;
public:
template<class... ARGS> inline void add(ARGS&&... args)
{list.emplace_back(std::forward<ARGS>(args)...);}
template<class... ARGS> inline void notifyAll(ARGS&&... args)
{for(auto&& x : list) x(args...);}
};
事件的小模板,仅支持添加侦听器并将其全部调用。
实例化
event<std::vector<void(*)()>> a;
event<std::vector<std::function<void()>>> a;
或任何其他可赎回的包装。
有用的更改是使用std::tuple<tag, callable>
的容器,其中必须在添加时提供标记,并且可以用于删除侦听器。
template<class F, class T=void*> class event {
std::vector<std::pair<T, F>> v;
public:
template<class... ARGS> inline void add(T tag, ARGS&&... args)
{v.emplace_back(std::piecewise_construct, std::make_tuple(tag),
std::forward_as_tuple(std::forward<ARGS>(args)...));}
template<class... ARGS> inline void notifyAll(ARGS&&... args)
{for(auto&& x : v) x(args...);}
void remove(T tag) {v.erase(std::remove_if(v.begin(), v.end(),
[=](const std::pair<T, F>& x){return x.first()==tag;}), v.end());}
};
后者使用仿函数类型实例化,如std::function<...>
或函数指针。
答案 1 :(得分:2)
新标准中的可变参数模板是解决问题的完美方式。您可以在此处查看使用模板的Observer模式实现:https://github.com/fnz/ObserverManager
答案 2 :(得分:1)
听众需要知道他们将从根本上调用的签名。捆绑消息集是有用的,但有时只值得。考虑一下这个界面:
template<class...Args>
struct broadcaster {
template<class F> // F:Args...->vpid
std::shared_ptr<void> attach( F&& f );
size_t operator()(Args... args)const;
};
现在,侦听器只需要将一个invokable传递给广播公司,然后在他们想要收听时存储一个共享的ptr。当共享ptr到期时,tue消息停止发送。
发件人使用参数调用广播公司。
template<class...Args>
struct broadcaster {
template<class F> // F:Args...->vpid
std::shared_ptr<void> attach( F&& f ){
auto x = std::make_shared<std::function<void(Args...)>>(std::forward<F>(f)));
funcs.push_back(x);
return x;
}
size_t operator()(Args... args)const{
funcs.erase(
std::remove_if(begin(funcs),end(funcs),
[](auto&&f){return f.expired();}
)
,end(funcs)
);
aito fs=funcs;
for(auto&&f:fs){
if(auto f_=f.lock())
f_(args...);
}
}
public:
mutable std::vector<std::weak_ptr<std::function<void(Args...)>>> funcs;
};
这是一口。
这确实意味着你的两种方法是分离的。但它们可以用简单的lambda连接起来。在监听类中存储std::vector<std::shared_ptr<void>>
以处理l8fetime管理问题。