本篇開始前先发个福利。程杰的《大话设计模式》一书高清电子版(带文件夹)已上传至CSDN,免积分下载。 下载地址: 代理模式是一种比較简单但却有用的设计模式,他能够灵活的更换代理的对象。但保证功能的完整性。就如卖衣服的代理商。他能够代理美特斯邦威的衣服,假设美特斯邦威的衣服被大家吐槽不好卖了,他还能够换去代理卖佐丹奴的,但无论怎么更换,还是能满足大家的需求——买衣服。 以下以大话设计模式书中的样例为例,设计一个代理帮小明送花给小红。
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#endif4、測试演示样例
void Test(){ IPursuit* poIXiaoMing = new CPursuit(_T("XiaoMing")); CProxy oCProxy; oCProxy.SetPursuit(poIXiaoMing); oCProxy.SendFlowers();}5、代理类的应用 这个样例非常形象,但却非常难看出代理模式的应用和长处。实际上在《 》中有一个操作数据库管理员工信息的样例,因为可能会在使用数据库的过程中切换数据库。如曾经用的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);}从使用演示样例中就能够看出,代理类支持客户使用过程中动态切换数据库,这是和工厂模式最大的一点不同。特别适用于在常常须要切换类似对象模式的地方。