如何将数据传递到';通用';观察者作为参数还是作为单个结构? 我正忙着向一个传统C++应用程序添加一个通用的观察机制(使用Visual Studio 2010,但不使用.NET,所以.NET委托是不可能的)。
在设计中,我希望尽可能将特定于应用程序的部分与通用观察者机制分离 实现观察者的最合乎逻辑的方式似乎是:如何将数据传递到';通用';观察者作为参数还是作为单个结构? 我正忙着向一个传统C++应用程序添加一个通用的观察机制(使用Visual Studio 2010,但不使用.NET,所以.NET委托是不可能的)。,c++,observer-pattern,observers,C++,Observer Pattern,Observers,在设计中,我希望尽可能将特定于应用程序的部分与通用观察者机制分离 实现观察者的最合乎逻辑的方式似乎是: class IDoThisObserver { public: void handlDoThis(int arg1, int arg2) = 0; }; 对于每种类型的观察者(IDoThisObserver,IDoThatObserver,…),方法(handleDoThis,handleDoThat)的参数都是不同的 以一种通用的方式存储观察者的剩余内容如下:
class IDoThisObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
对于每种类型的观察者(IDoThisObserver,IDoThatObserver,…),方法(handleDoThis,handleDoThat)的参数都是不同的
以一种通用的方式存储观察者的剩余内容如下:
template<typename T>
class ObserverContainer
{
public:
void addObserver (T &t) {m_observers.push_back(&t);}
private:
std::list<T*> m_observers;
};
struct DoThisInfo
{
DoThisInfo (int arg1, int arg2) : m_arg1(arg1), m_arg2(arg2) {}
int m_arg1;
int m_arg2;
};
template<typename T>
class IObserver
{
public:
void notify(const T &t) = 0;
};
class MyObserver : public IObserver<NotifyThis>, public IObserver<NotifyThat>
{
...
};
然后定义一个更通用的观察者,如下所示:
template<typename T>
class ObserverContainer
{
public:
void addObserver (T &t) {m_observers.push_back(&t);}
private:
std::list<T*> m_observers;
};
struct DoThisInfo
{
DoThisInfo (int arg1, int arg2) : m_arg1(arg1), m_arg2(arg2) {}
int m_arg1;
int m_arg2;
};
template<typename T>
class IObserver
{
public:
void notify(const T &t) = 0;
};
class MyObserver : public IObserver<NotifyThis>, public IObserver<NotifyThat>
{
...
};
模板
类IObserver
{
公众:
无效通知(常数T&T)=0;
};
这些观察者的集合将变成:
template<typename T>
class ObserverContainer
{
public:
void addObserver (IObserver<T> &obs) {m_observers.push_back(&obs);}
private:
std::list<IObserver<T>*> m_observers;
};
模板
类ObserverContainer
{
公众:
void addObserver(IObserver&obs){m_observer.push_back(&obs)}
私人:
标准:列出m_观察员;
};
现在,更多的逻辑可以集中添加到这个observer容器中,包括调用所有观察者。调用的“发起人”只需要创建和填写通知结构
要从多种类型的观察者继承的类需要这样做:
template<typename T>
class ObserverContainer
{
public:
void addObserver (T &t) {m_observers.push_back(&t);}
private:
std::list<T*> m_observers;
};
struct DoThisInfo
{
DoThisInfo (int arg1, int arg2) : m_arg1(arg1), m_arg2(arg2) {}
int m_arg1;
int m_arg2;
};
template<typename T>
class IObserver
{
public:
void notify(const T &t) = 0;
};
class MyObserver : public IObserver<NotifyThis>, public IObserver<NotifyThat>
{
...
};
class MyObserver:public IObserver,public IObserver
{
...
};
以下哪种方法(具有多个显式参数的观察者或具有一个结构参数的观察者)似乎是最好的?这两种方法都有优点或缺点吗
编辑:我进一步研究了其他方法,插槽/信号方法似乎是另一个不错的选择。插槽/信号是否有我应该知道的重要缺点?为什么不这样做:
class IObserver {
// whatever is in common
};
class IDoThisObserver : public IObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
class IDoThatObserver : public IObserver
{
public:
void handlDoThat(double arg1) = 0;
};
?
那么你有:
class ObserverContainer
{
public:
void addObserver (IObserver* t) {m_observers.push_back(t);}
private:
std::list<IObserver*> m_observers;
};
类observer容器
{
公众:
void addObserver(IObserver*t){m_observer.push_back(t)}
私人:
标准:列出m_观察员;
};
带有struct
参数的设计肯定更好,因为它允许在ObserverContainer
中编写通用代码。通常,用封装参数的对象替换较长的参数列表是一种很好的设计实践,这是一个很好的回报示例。通过为notify
方法创建一个更一般的抽象(使用struct,您将notify
定义为一个获取一大块“数据”的方法,而使用arg list,您将定义一个获取两个数字的方法)您可以编写使用该方法的通用代码,而不必关心传入数据块的确切组成。我没有正确的语法,所以我只列出声明来说明结构。一个通用的观察者可以期望一个参数,该参数或者是所需参数的特定形式的子类,或者是包含观察者所需的所有基本参数的水平映射的结构。然后,ObserverContainer可以用作,ObserverContainer的每个子类可以是DoThatObserverFactory和DoThisObserverFactory。工厂将构建一个观察器,并为观察器分配一个配置,以告诉它需要哪个参数
class AbstractObserverFactory {...};
class DoThatObserverFactory : AbstractObserverFactory {...};
class DoThisObserverFactory : AbstractObserverFactory {...};
class ObserverParam {...};
class DoThatObserverParam : ObserverParam {...};
class DoThisObserverParam : ObserverParam {...};
class Observer;
class DoThisObserver : public Observer
{
public:
void handlDoThis(DoThisObserverParam);
};
我认为你的两种方法都不符合你的要求。然而,使用一个包含数据集的数据载体对所有观察者进行一点修改,其中每个观察者都知道要读取什么,就可以做到这一点。下面的示例代码可能会清除它(注意,我还没有编译)
枚举类型{
通知你,,
通知你
};
结构数据{
虚拟类型getType()=0;
};
结构NotifyThisData:公共数据{
NotifyThisData(int_a,int_b):a(_a),b(_b){
INTA,b;
键入getType(){return NOTIFY_THIS;}
};
结构NotifyThatData:公共数据{
NotifyThatData(std::string _str):str(_str){
std::字符串str;
键入getType(){return NOTIFY_THAT;}
};
结构数据载体{
std::向量m_类型数据;
};
类IObserver{
公众:
虚空句柄(数据载体和数据)=0;
};
类NotifyThis:公共虚拟IObserver{
公众:
虚拟无效句柄(数据载体和数据){
vector::iterator iter=find_if(data.m_TypeData.begin(),data.m_TypeData.end(),bind2nd(functor(),NOTIFY_THIS);
if(iter==data.m_TypeData.end())
返回;
NotifyThisData*d=动态投影(*iter);
std::你有没有研究过增压信号?比重新安装车轮更好
至于参数:调用观察者/插槽在概念上应该与调用普通函数相同。大多数SignalSlot实现允许多个参数,因此使用它。请为不同的观察者类型使用不同的信号,这样就不需要在变量中传递数据
我已经看到了观察者模式/信号槽的两个缺点:
1) 仅通过查看源代码,很难甚至不可能理解程序流程。
2) 具有大量观察者/信号槽的高度动态程序可能会遇到“删除此项”
除此之外,我更喜欢观察者/信号槽,而不是子类化和高耦合。这只是将通用代码放在IObserver中。我正在寻找一种将通用代码放在ObserverContainer中的方法。这种方法很好,只要不需要迭代观察者并在某些常规情况下调用它们的处理方法-是这样。那将是一场噩梦。问题是ObserverContainer
无法在没有大量强制转换的情况下调用观察器上的handle
方法。@bshiels,如果他不知道它们采用什么参数,他怎么能强制转换handle?@Kornel The primary fu