从观察者模式的实现,看c/c++的语言特性

观察者模式,是我们在开发过程中经常会遇到或者使用的设计模式。

比较常见的,我们在GUI库里便能看到其身影,譬如:键盘敲击事件后,需要将这一事件通知到所有观测键盘事件的对象。但凡涉及到这种一对多的通知,基本上都是观察者模式的应用。

更详细的介绍请参考:

http://blog.csdn.net/blacklaw0/article/details/8870210


我们经常使用的开发库中经常会存在这种模式,但是实现方式却不尽相同。所以,了解c/c++语言下观察者模式的实现方式,对于我们学习和掌握各种开源库将非常有帮助。一旦了解到该模式的存在,对整个代码会更增加一层了解和信心。


下面,我以我所知道的几种方式来分别实现一个非常简单的观察者模式,以示语言特性和该模式的一些特征。

假设,Subject会接受整数A和B,同时该Subject绑定了若干个Observer,在接受到整数A和B之后,需要及时通知所有Observer(例子里作为观察者的Adder和Muler皆简单输出A和B的和以及乘积)


先看c风格的代码实现:

#include <stdio.h>

// c style
int Add(int iA, int iB)
{
	printf("Add(%d,%d)=%d\n", iA, iB, iA + iB);
	return iA + iB;
}
int Mul(int iA, int iB)
{
	printf("Mul(%d,%d)=%d\n", iA, iB, iA * iB);
	return iA * iB;
}
typedef int (*fpCallBack)(int iA, int iB);

int main()
{
	fpCallBack fpcb[2] = {&::Add, &::Mul};
	fpcb[0](1, 2);
	fpcb[1](1, 2);
        return 0;}

这种方式间接明了,一路了然。c的风格就是这样的,不同的人写一小段代码,可能都大同小异。


#include <stdio.h>
#include <vector>

// cpp style1
struct IObserver1
{
	virtual int OnAB(int iA, int iB)=0;
};
struct Subject1
{
	void AddObserver(IObserver1*pObserver)
	{
		m_vecObserver.push_back(pObserver);
	}
	void Notify(int iA, int iB)
	{
		for (unsigned i = 0; i < m_vecObserver.size(); ++i)
		{
			m_vecObserver[i]->OnAB(iA, iB);
		}
	}
	std::vector<IObserver1*> m_vecObserver;
};
struct Adder1 : public IObserver1
{
	virtual int OnAB(int iA, int iB)
	{
	    printf("Add1(%d,%d)=%d\n", iA, iB, iA + iB);
		return iA + iB;
	}
};
struct Muler1 : public IObserver1
{
	virtual int OnAB(int iA, int iB)
	{
		printf("Mul1(%d,%d)=%d\n", iA, iB, iA * iB);
		return iA * iB;
	}
};

int main()
{
	Subject1 subj1;
	Adder1 adder1;
	Muler1 muler1;
	subj1.AddObserver(&adder1);
	subj1.AddObserver(&muler1);
	subj1.Notify(1, 2);
        return 0;
}
这个也是比较显而易见的方式,定义一个虚基类,各个观察者必须继承并实现该接口,以方便管理并及时收到通知。这种方式的可读性比较好,但是耦合性比较大。


再看一个c++实现的方式:

#include <stdio.h>
#include <vector>

// cpp style2
struct IObserver2
{
};
typedef int (IObserver2::*Ofp)(int iA, int iB);

struct Subject2
{
	void AddObserver(IObserver2*pObj, Ofp fp)
	{
		m_vecpObj.push_back(pObj);
		m_vecOfp.push_back(fp);
	}
	void Notify(int iA, int iB)
	{
		for (unsigned i = 0; i < m_vecpObj.size(); ++i)
		{
			(((IObserver2*)m_vecpObj[i])->*m_vecOfp[i])(iA, iB);
		}
	}
	std::vector<void*> m_vecpObj;
	std::vector<Ofp> m_vecOfp;
};

struct Adder2 : public IObserver2
{
	int OnAB(int iA, int iB)
	{
	    printf("Add2(%d,%d)=%d\n", iA, iB, iA + iB);
		return iA + iB;
	}
};
struct Muler2 : public IObserver2
{
	int OnAB(int iA, int iB)
	{
		printf("Mul2(%d,%d)=%d\n", iA, iB, iA * iB);
		return iA * iB;
	}
};

int main()
{
	Subject2 subj2;
	Adder2 adder2;
	Muler2 muler2;
	subj2.AddObserver(&adder2, (Ofp)&Adder2::OnAB);
	subj2.AddObserver(&muler2, (Ofp)&Muler2::OnAB);
	subj2.Notify(1, 2);
        return 0
}
这个方法是基于类的成员函数指针来实现的,同样是继承,但是并不要求Observer类必须实现制定的虚函数,只需要保证有相同的成员函数签名。一个Observer类甚至能容纳多个Observer方法。这种方式相对前一种方式更灵活一点,但是同样要求继承一个基类,还是有一定的耦合性。


再看最后一个,c++11的版本

// cpp11
typedef std::function<int(int, int)> IntOpFun;

struct Subject3
{
	void AddObserver(IntOpFun of)
	{
		m_vecOf.push_back(of);
	}

	void Notify(int iA, int iB)
	{
		for (auto iter = m_vecOf.begin(); iter != m_vecOf.end(); ++iter)
		{
			(*iter)(iA, iB);
		}
	}
	std::vector<IntOpFun> m_vecOf;
};
struct Adder3
{
	int OnAB(int iA, int iB)
	{
	    printf("Add3(%d,%d)=%d\n", iA, iB, iA + iB);
		return iA + iB;
	}
};

struct Muler3
{
	int OnAB(int iA, int iB)
	{
		printf("Mul3(%d,%d)=%d\n", iA, iB, iA * iB);
		return iA * iB;
	}
};

int main()
{
	Subject3 subj;
	Adder3 adder;
	Muler3 muler;
	subj.AddObserver(std::bind(&Adder3::OnAB, &adder, std::placeholders::_1, std::placeholders::_2));
	subj.AddObserver(std::bind(&Muler3::OnAB, &muler, std::placeholders::_1, std::placeholders::_2));
	subj.Notify(1, 2);
	return 0;
}
这种方式不需要继承基类,不需要实现指定接口,通过std:fuction和std::bind可以发挥出相当大的自由度。但是在代码易读性上有一定损失。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值