代理模式是一种比較简单但却有用的设计模式。他能够灵活的更换代理的对象,但保证功能的完整性。就如卖衣服的代理商,他能够代理美特斯邦威的衣服,假设美特斯邦威的衣服被大家吐槽不好卖了,他还能够换去代理卖佐丹奴的,但无论怎么更换,还是能满足大家的需求——买衣服。

    以下以大话设计模式书中的样例为例,设计一个代理帮小明送花给小红。


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);
}

    从使用演示样例中就能够看出,代理类支持客户使用过程中动态切换数据库,这是和工厂模式最大的一点不同,特别适用于在常常须要切换类似对象模式的地方。