更稳定的观察者模式的模板方法
使用模板解决观察者模式中的问题。
引言
观察者模式(也称为“发布-订阅”或“主题-观察者”模式)是开发者社区中最著名的模式之一。也有很多文章讨论它。如果您对观察者模式不是很熟悉,您可以先查看这里。在本文中,我想展示一个稍微更稳定的观察者模式版本,它在处理动态变化的主题-观察者关系时特别有用且省时。
在标准实现中,主题持有一个指向已附加观察者的指针数组。一个已附加的观察者应该在死亡之前显式地从主题“分离”自己。所以,观察者需要以某种方式持有返回到主题的引用。你可能会意识到,双方都持有对彼此的引用。如果任何主题或观察者在沉默中死亡 -- 比如说,一个抛出的异常跳过了'detach'调用。悬挂指针!
我的模板实现为您提供:观察者不持有对主题的引用,并且主题可以在其附加的观察者之前静默死亡。我的模板还提供了标准功能 -- 自动实现支持代码(例如,附加/分离和通知)。
实现
附加的源代码应该能够解释它自己。在这里,我想展示一些基本的想法。
如上图所示,每个主题使用一个双向列表。双向列表的好处是观察者可以随时从链中取消链接自己。因此,可以从观察者的析构函数中调用delink函数,这确保列表中不会留下悬挂指针。
观察者本身实际上并没有直接链接到列表中。相反,观察者拥有一个可链接节点的数组,这些节点依次链接到列表中。这些节点也充当代理,将通知消息转发给它的所有者,即观察者。
Using the Code
使用模板非常简单。在我向您展示一个简单的例子之前,请确保您已经安装了boost库。假设在一个股票市场应用程序中,您需要编写一个价格监视器(一个观察者),以便在最新交易价格发生变化时提醒用户。下面显示了需要实现的接口。
struct PriceChangeListener
{
virtual void OnChange(double latest_price) = 0;
};
一个价格监视器看起来像这样
class PriceMoniter : public LinkableObserverImpl
{
virtual void OnChange(double latest_price)
{
cout << "Latest price changed to " << latest_price;
}
}
一个内容主题看起来像这样
#include <boost/bind.hpp>
class PricePublisher : public SubjectSourceImpl<PriceChangeListener>
{
public:
void SetLastestPrice(double latest_price_))
{
NotifyChanges(double latest_price_));
}
private:
void NotifyChanges(double latest_price_)
{
TravelObservers(boost::bind(&PriceChangeListener::OnChange, _1, latest_price_)));
}
}
请注意,PricePublisher
的所有附加和分离函数都由SubjectSourceImpl
模板提供。PricePublisher
唯一需要实现的函数是NotifyChanges
,它调用已附加观察者的OnChange
函数。在这里,我使用boost::bind
来简化调用。
下面的代码片段显示了如何将观察者附加到主题。
PricePublisher subject_A;
{
PriceMoniter observer_1;
subject_A.Attach(&observer_1);
// processing ...
// ...
// ...
subject_A.SetLastestPrice(2.35); //trigger observer 1
}
PriceMoniter observer_2;
subject_A.Attach(&observer_2);
subject_A.SetLastestPrice(2.40); //trigger observer 2 only
//....
局部变量observer_1
在一个代码块中,该代码块将在该代码块之外立即销毁。您可以看到,没有必要“分离”observer_1
。
您可能会注意到,该代码比标准的观察者模式实现更短。所以,尽情使用这些模板吧。
Notice
- 尽量不要在不同的线程中创建观察者和主题,因为我在操作链表时没有实现任何互斥操作。当然,您可以随意添加此功能。
- 我使用boost库来简化我的日常生活。如果您不习惯boost库,也可以随意更改为无boost的版本。