如何解决在观察者模式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 方法,在我看来,它在这里被过度使用了,因为您没有资源来释放、每次创建和处置对象对于您的需求来说都太过分了。 顺便说一句,如果你想走这条路,那么我建议你修复一些代码异味。
- 您没有正确封装
SetTopLevelCall
。它不应该是public
,因为您的类的用户不能弄乱它。 - 有一个新的公共类
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 个事件被发送。
我使用的方法是我在每个DeferredEventSender
方法中放了一个ChangeMethodX
本地对象,如果是顶级函数调用,这个对象会有它的成员变量m_TopLevelCallScope
设置为true,如果是内部函数调用,则m_TopLevelCallScope
设置为false。
当 DeferredEventSender
本地对象离开作用域时,它会检查它是否是顶级对象,如果为真,它将发送事件,因此所有内部函数调用都不会发送事件。
可以扩展演示代码,以便在std::queue<Event>
对象或DeferredEventSender
对象中的Model
中累积和存储事件,并且当顶部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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。