在观察者模式(c++)中,充斥着更新请求的视图



我有一些MVC代码,它使用观察者模式,如:

void Model::ChangeMethod1()
{
m_A = m_A + 1;
...
Notify();
}
void Model::ChangeMethod2()
{
m_A = m_A + 2;
...
Notify();
}
void Model::ChangeMethod3()
{
ChangeMethod1();
ChangeMethod2();
Notify();
}
void Model::ChangeMethod4()
{
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
Notify();
}

有许多功能,如ChangeMethodX,将对模型进行更改,并通知查看器,当查看器接收到事件时,它们将刷新/更新自己。

你看,每个函数ChangeMethodX都有一个Notify()函数,它在内部发送一个事件给观察者。

但是我不希望观察者在每个函数中接收太多的事件,因为会有太多的事件,我希望每个顶级函数调用,无论它是否有任何内部函数调用,只发送一个更新事件给查看器。

我认为这是一个非常常见的问题,发生在许多情况下,如MVC模式,作为一个模型将通知观众得到更新。但是,如果模型在顶级函数调用中多次更改,我们必须避免泛滥事件。

我想到了两种可能的方法:

如果主题完全在您的控制之下,并且此解决方案不是太侵入,您可以添加一个可选参数,指定所调用的ChangeMethodX是否是顶级函数,如:

void Model::ChangeMethod1(bool topLevel = true)
{
m_A = m_A + 1;
...
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod2(bool topLevel = true)
{
m_A = m_A + 2;
...
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod3(bool topLevel = true)
{
ChangeMethod1(false);
ChangeMethod2(false);
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod4(bool topLevel = true)
{
ChangeMethod1(false);
ChangeMethod2(false);
ChangeMethod3(false);
NotifyIfTopLevel(topLevel);
}
void Model::NotifyIfTopLevel(bool topLevel)
{
if (topLevel)
Notify();
}

然而,大多数时候它是丑陋的,它可能会弄脏你的界面。


另一方面,如果必须处理并发性,可以选择的第二种方法是有风险的。此外,如果你捕捉到一个异常并处理它,你必须记住把对象带回一个正确的状态(如果还没有调用is_changing--),否则观察者将不会再收到通知。

int is_changing = 0;
void Model::ChangeMethod1()
{
m_A = m_A + 1;
...
NotifyIfNotChanging();
}
void Model::ChangeMethod2()
{
m_A = m_A + 2;
...
NotifyIfNotChanging();
}
void Model::ChangeMethod3()
{
is_changing++;
ChangeMethod1();
ChangeMethod2();
is_changing--;
NotifyIfNotChanging();
}
void Model::ChangeMethod4()
{
is_changing++;
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
is_changing--;
NotifyIfNotChanging();
}
void Model::NotifyIfNotChanging()
{
if (is_changing == 0)
Notify();
}

如果你有那么多ChangeMethodX方法,也许可以考虑使用面向方面的框架来分离通知观察者的关注点。特别是如果您需要重复is_changing++/--或平凡的Notify调用,将它们移动到适当的方面类中肯定会更具可读性。


编辑

至于RAII方法,在我看来,它在这里被过度使用了,因为您没有资源可以释放,每次创建和处置对象对于您的需求来说都是相当多余的。顺便说一下,如果您想遵循这条路径,那么我建议您修复一些代码异味。

  1. 您没有正确封装SetTopLevelCall。它不应该是public,因为你的类的用户不能乱动它。
  2. 有一个新的公共类DeferredEventSender,它与你的Model类紧密耦合。最糟糕的部分是它负责Notify方法,该方法应该由Model本身调用。此外,您排除了需要访问Model私有字段和函数的可能性。

以下是我将如何面对这些问题,尽管它还不完美。

class Model
{
public:
Model()
{
}

~Model()
{
}

void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();

void Notify();

protected:

class DeferredEventSender
{
public:

DeferredEventSender(Model* m)
{
_m = m;
doCallNotify = _m->topLevel;
_m->topLevel = false;
}

~DeferredEventSender()
{
if (doCallNotify)
{
_m->Notify();
_m->topLevel = true;
}
}

Model* _m;
bool doCallNotify;
};

bool topLevel = true;

int m_A;
int m_B;
};

void Model::ChangeMethod1()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 1;
}
...

我只是遵循Marco Luzzara的第二种方法,并创建了一个简单的c++演示代码,见下文:

修订1:

#include <iostream>
using namespace std;
class Model
{
public:
Model()
: m_TopLevelCallScope(false)
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
bool IsTopLevelCall()
{
return m_TopLevelCallScope;
}
void SetTopLevelCall(bool topLevel)
{
m_TopLevelCallScope = topLevel;
}
private:
// if this variable is true, it means a top level call scope is entered
// then all the inner call should not send event, the final event could
// send when the top level sender get destructed
bool m_TopLevelCallScope;
// other members
int m_A;
int m_B;
};

// this is a deferred notification
// each function should create a local object
// but only the top level object can finally send a notification
class DeferredEventSender
{
public:
DeferredEventSender(Model* model)
: m_Model(model)
{
if(m_Model->IsTopLevelCall() == false)
{
m_Model->SetTopLevelCall(true);
m_TopLevelCallScope = true;
}
else
{
m_TopLevelCallScope = false;
}
}
~DeferredEventSender()
{
if (m_TopLevelCallScope == true)
{
// we are exiting the top level call, so restore it to false
// it's time to send the notification now
m_Model->SetTopLevelCall(false);
m_Model->Notify();
}
// do nothing if m_TopLevelCallScope == false
// because this means we are in a inner function call
}
bool m_TopLevelCallScope;
Model* m_Model;
};

void Model::ChangeMethod1()
{
DeferredEventSender sender(this);
m_A = m_A + 1;
}
void Model::ChangeMethod2()
{
DeferredEventSender sender(this);
m_A = m_A + 2;
}
void Model::ChangeMethod3()
{
DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
}
void Model::ChangeMethod4()
{
DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
}
void Model::Notify()
{
cout << "Send event!" << endl;
}
int main()
{
Model m;
m.ChangeMethod1();
m.ChangeMethod2();
m.ChangeMethod3();
m.ChangeMethod4();
return 0;
}
下面是演示c++代码的输出:
Send event!
Send event!
Send event!
Send event!

你可以看到在main()函数中,我只有4个函数调用,并且只有4个事件被发送。

我使用的方法是,我在每个ChangeMethodX方法中放置一个DeferredEventSender本地对象,如果它是一个顶级函数调用,该对象将其成员变量m_TopLevelCallScope设置为true,如果它是一个内部函数调用,m_TopLevelCallScope设置为false。

DeferredEventSender局部对象离开作用域时,它将检查它是否是顶级对象,如果为true,它将发送事件,因此所有内部函数调用都不会发送事件。

演示代码可以扩展,使事件可以累积并存储在DeferredEventSender对象或Model对象中的std::queue<Event>中,当顶部DeferredEventSender对象被破坏时,我们可以在std::queue<Event>中运行过滤器,删除重复的事件,并发送我们实际需要的事件。


根据Marco Luzzara的建议,这是修改后的版本,谢谢Marco Luzzara!

修订2:

#include <iostream>
using namespace std;
class Model
{
public:
Model()
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
protected:
class DeferredEventSender
{
public:
DeferredEventSender(Model* m)
{
m_Model = m;
// the first instance of the DeferredEventSender will copy the status of m_TopLevel
// and all the later(inner) instances will have false m_TopLevel
m_DoCallNotify = m_Model->m_TopLevel;
m_Model->m_TopLevel = false;
}
~DeferredEventSender()
{
// we only call Notify on the top level DeferredEventSender
if (m_DoCallNotify)
{
m_Model->Notify();
m_Model->m_TopLevel = true;
}
}
Model* m_Model;
bool m_DoCallNotify;
};
bool m_TopLevel = true;
int m_A;
int m_B;
};

void Model::ChangeMethod1()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 1;
}
void Model::ChangeMethod2()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 2;
}
void Model::ChangeMethod3()
{
Model::DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
}
void Model::ChangeMethod4()
{
Model::DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
}
void Model::Notify()
{
cout << "Send event!" << endl;
}
int main()
{
Model m;
m.ChangeMethod1();
m.ChangeMethod2();
m.ChangeMethod3();
m.ChangeMethod4();
return 0;
}

相关内容

  • 没有找到相关文章