大話設計模式C++版——代理模式

    本篇開始前先發個福利,程傑的《大話設計模式》一書高清電子版(帶目錄)已上傳至CSDN,免積分下載。
下載地址:http://download.csdn.net/detail/gufeng99/8843487

    代理模式是一種比較簡單但卻實用的設計模式,他可以靈活的更換代理的對象,但保證功能的完整性,就如賣衣服的代理商,他可以代理美特斯邦威的衣服,如果美特斯邦威的衣服被大家吐槽不好賣了,他還可以換去代理賣佐丹奴的,但不管怎麼更換,還是能滿足大家的需求——買衣服。
    下面以大話設計模式書中的例子爲例,設計一個代理幫小明送花給小紅。

1、依據接口編程,設計代理對象的接口

class IPursuit
{
public:
	virtual	~IPursuit()	{}

	virtual	void	SendFlowers() = 0;
};
2、代理類,也繼承代理對象類,保持接口一致
class CProxy : public IPursuit
{
public:
	CProxy() : m_poIPursuit(NULL)	{}
	~CProxy()
	{
		if (m_poIPursuit)
		{
			delete	m_poIPursuit;
			m_poIPursuit = NULL;
		}
	}
	
	void	SetPursuit(IPursuit* poIPursuit)
	{
		//如果有舊的代理,要先刪除,否則會造成內存泄漏
		if (m_poIPursuit)
		{
			delete	m_poIPursuit;
		}

		m_poIPursuit = poIPursuit;
	}

	void	SendFlowers()
	{
		if (m_poIPursuit)
		{
			printf("Proxy help ");
			m_poIPursuit->SendFlowers();
		}
	}

private:
	IPursuit*	m_poIPursuit;
};
    代理類實際上啥也沒幹,只是對同樣的函數調用了一手被代理的對象的對應函數,當了一回二傳手的角色。這裏要注意代理對象由於會在代理中被釋放,所以代理的對象一律必須是new出來的,即需在堆上創建的。
3、被代理對象類
class CPursuit : public IPursuit
{
public:
	CPursuit(TString tstrName) : m_tstrName(tstrName) {}
	~CPursuit()	{}

	void	SendFlowers()
	{
		_tprintf(_T("%s sent flowers to Xiaohong\n"), m_tstrName.c_str());
	}

private:
	TString	m_tstrName;
};
另附上TString宏
#ifdef  UNICODE
	#define	TString	std::wstring
#else
	#define	TString	std::string
#endif
4、測試示例
void	Test()
{
	IPursuit*	poIXiaoMing = new CPursuit(_T("XiaoMing"));
	CProxy		oCProxy;
	
	oCProxy.SetPursuit(poIXiaoMing);
	oCProxy.SendFlowers();
}
5、代理類的應用
    這個例子很形象,但卻很難看出代理模式的應用和優點。實際上在《大話設計模式C++版——抽象工廠模式》中有一個操作數據庫管理員工信息的例子,由於可能會在使用數據庫的過程中切換數據庫,如以前用的MySql,可能某個客戶要求支持Access,這時就得進行切換了,此時用代理模式一樣可以實現。
5.1 代理模式實現員工數據庫管理類對數據庫的切換
typedef	struct Employee 
{
	int		nID;
	TString	tstrName;
};

class IEmployee
{
public:
	~IEmployee()	{}
	
	virtual	bool		InserttoDB(Employee& stEmployee) = 0;
	virtual	Employee	GetEmployee(int nID) = 0;
};

class CProxy : public IEmployee
{
public:
public:
	CProxy() : m_poIEmployee(NULL)	{}
	~CProxy()
	{
		if (m_poIEmployee)
		{
			delete	m_poIEmployee;
			m_poIEmployee = NULL;
		}
	}
	
	void		SetEmployee(IEmployee* poIEmployee)
	{
		if (m_poIEmployee)
		{
			delete	m_poIEmployee;
		}

		m_poIEmployee = poIEmployee;
	}

	bool		InserttoDB(Employee& stEmployee)
	{
		if (m_poIEmployee)
		{
			return	m_poIEmployee->InserttoDB(stEmployee);
		}
		
		return	false;
	}

	Employee	GetEmployee(int nID)
	{
		if (m_poIEmployee)
		{
			return	m_poIEmployee->GetEmployee(nID);
		}
		
		Employee	stEmployee;
		return	stEmployee;
	}

private:
	IEmployee*	m_poIEmployee;
};

class CEmployeefromMysql : public IEmployee
{
public:
	bool		InserttoDB(Employee& stEmployee)
	{
		_tprintf(_T("Insert employee %s into mysql\n"), stEmployee.tstrName.c_str());
		return	true;
	}
	
	Employee	GetEmployee(int nID)
	{
		Employee	stEmployee;
		printf("Get an employee from mysql by id %d\n", nID);
		return	stEmployee;
	}
};

class CEmployeefromAccess : public	IEmployee
{
public:
	bool		InserttoDB(Employee& stEmployee)
	{
		_tprintf(_T("Insert employee %s into access\n"), stEmployee.tstrName.c_str());
		return	true;
	}
	
	Employee	GetEmployee(int nID)
	{
		Employee	stEmployee;
		printf("Get an employee from access by id %d\n", nID);
		return	stEmployee;
	}
};
5.2 使用示例
void	DataBaseTest()
{
	IEmployee*	poIEmployee = new CEmployeefromMysql();
	CProxy		oCProxy;

	oCProxy.SetEmployee(poIEmployee);
	
	Employee	stEmployee;
	stEmployee.nID = 1;
	stEmployee.tstrName = _T("Jim");
	
	oCProxy.InserttoDB(stEmployee);
	
	//切換數據庫對象
	poIEmployee	= new	CEmployeefromAccess();
	
	oCProxy.SetEmployee(poIEmployee);
	oCProxy.InserttoDB(stEmployee);
}
    從使用示例中就可以看出,代理類支持客戶使用過程中動態切換數據庫,這是和工廠模式最大的一點不同,特別適用於在經常需要切換類似對象模式的地方。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章