下載地址: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);
}
從使用示例中就可以看出,代理類支持客戶使用過程中動態切換數據庫,這是和工廠模式最大的一點不同,特別適用於在經常需要切換類似對象模式的地方。