Visitor模式詳解--設計模式(20)
Visitor模式來源:
在面向物件系統的開發和設計過程,經常會遇到一種情況就是需求變更(RequirementChanging),經常我們做好的一個設計、實現了一個系統原型,咱們的客戶又會有了新的需求。我們又因此不得不去修改已有的設計,最常見就是解決方案就是給已經設計、實現好的類新增新的方法去實現客戶新的需求,這樣就陷入了設計變更的夢魘:不停地打補丁,其帶來的後果就是設計根本就不可能封閉、編譯永遠都是整個系統程式碼。Visitor模式則提供了一種解決方案。
Visitor模式作用:
將更新(變更)封裝到一個類中(訪問操作),並由待更改類提供一個接收介面,則可達到效果。
Visitor
Visitor模式構成:
抽象訪問者(Visitor):抽象出訪問元素的動作
具體訪問者(ConcreteVisitor):實現訪問元素的動作
抽象元素(Element):定義一個接受訪問的操作,其引數為訪問者
具體元素(ConcreteElement):實現接受訪問操作
物件結構類(ObjectStructure):可以列舉元素,並且管理元素
客戶端(Client) :定義元素集合,然後接受不同訪問者的訪問
Visitor模式原始碼示例:
Visitor.h
#pragma once class CCommonEmployee; class CManager; class IVisitor { public: IVisitor(void) { } virtual ~IVisitor(void) { } virtual void Visit(CCommonEmployee commonEmployee) = 0; virtual void Visit(CManager manager) = 0; virtual int GetTotalSalary() = 0; }; //BaseVisitor.h #pragma once #include "ivisitor.h" #include "CommonEmployee.h" #include "Manager.h" #include <iostream> using std::string; class CBaseVisitor : public IVisitor { public: CBaseVisitor(void); ~CBaseVisitor(void); void Visit(CCommonEmployee commonEmployee); void Visit(CManager manager); int GetTotalSalary(); private: string GetBasicInfo(CEmployee *pemployee); string GetManagerInfo(CManager manager); string GetCommonEmployee(CCommonEmployee employee); static const int MANAGER_COEFFICENT = 5; static const int COMMONEMPLOYEE_COEFFICENT = 2; int m_commonTotal; int m_managerTotal; void CalCommonSalary(int salary); void CalManagerSalary(int salary); }; //BaseVisitor.cpp #include "StdAfx.h" #include "..\CommonDeclare\Convert.h" #include "BaseVisitor.h" #include <iostream> using std::string; using std::cout; using std::endl; CBaseVisitor::CBaseVisitor(void) { m_commonTotal = 0; m_managerTotal = 0; } CBaseVisitor::~CBaseVisitor(void) { } void CBaseVisitor::Visit(CCommonEmployee commonEmployee) { cout << this->GetCommonEmployee(commonEmployee).c_str() << endl; this->CalCommonSalary(commonEmployee.GetSalary()); } void CBaseVisitor::Visit(CManager manager) { cout << this->GetManagerInfo(manager).c_str() << endl; this->CalManagerSalary(manager.GetSalary()); } string CBaseVisitor::GetBasicInfo(CEmployee *pemployee) { string info = ""; info.append("姓名:"); info.append(pemployee->GetName()); info.append("\t"); info.append("性別:"); info.append(CConvert::ToString(pemployee->GetSex())); info.append("\t"); info.append("薪水:"); info.append(CConvert::ToString(pemployee->GetSalary())); info.append("\t"); return info; } string CBaseVisitor::GetManagerInfo(CManager manager) { string basicInfo = this->GetBasicInfo(&manager); string otherInfo = ""; otherInfo.append("業績:"); otherInfo.append(manager.GetPerformance()); otherInfo.append("\t"); basicInfo.append(otherInfo); return basicInfo; } string CBaseVisitor::GetCommonEmployee(CCommonEmployee employee) { string basicInfo = this->GetBasicInfo(&employee); string otherInfo = ""; otherInfo.append("工作:"); otherInfo.append(employee.GetJob()); otherInfo.append("\t"); basicInfo.append(otherInfo); return basicInfo; } int CBaseVisitor::GetTotalSalary() { return this->m_commonTotal + this->m_managerTotal; } void CBaseVisitor::CalCommonSalary(int salary) { this->m_commonTotal += salary; } void CBaseVisitor::CalManagerSalary(int salary) { this->m_managerTotal += salary; }
//Employee.h #pragma once #include "Visitor.h" #include <iostream> using std::string; class CEmployee { public: static int MALE; static int FEMALE; CEmployee(void); virtual ~CEmployee(void); string GetName(); void SetName(string name); int GetSalary(); void SetSalary(int v); int GetSex(); void SetSex(int v); virtual void Accept(IVisitor *pVisitor) = 0; private: string m_name; int m_salary; int m_sex; }; //Employee.cpp #include "StdAfx.h" #include "Employee.h" int CEmployee::MALE = 0; int CEmployee::FEMALE = 1; CEmployee::CEmployee(void) { } CEmployee::~CEmployee(void) { } string CEmployee::GetName() { return m_name; } void CEmployee::SetName( string name ) { m_name = name; } int CEmployee::GetSalary() { return m_salary; } void CEmployee::SetSalary( int v ) { m_salary = v; } int CEmployee::GetSex() { return m_sex; } void CEmployee::SetSex( int v ) { m_sex = v; } //Manager.h #pragma once #include "employee.h" #include "IVisitor.h" #include <iostream> using std::string; class CManager : public CEmployee { public: CManager(void); ~CManager(void); string GetPerformance(); void SetPerformance(string per); void Accept(IVisitor *pVisitor); protected: string GetOtherInfo(); private: string m_performance; }; //Manager.cpp #include "StdAfx.h" #include "Manager.h" #include <iostream> using std::string; CManager::CManager(void) { this->m_performance = ""; } CManager::~CManager(void) { } string CManager::GetPerformance() { return m_performance; } void CManager::SetPerformance(string per) { this->m_performance = per; } string CManager::GetOtherInfo() { string info = ""; info.append("業績:"); info.append(this->m_performance); info.append("\t"); return info; } void CManager::Accept(IVisitor *pVisitor) { pVisitor->Visit(*this); } // Visitor.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include "Employee.h" #include "CommonEmployee.h" #include "Manager.h" #include "BaseVisitor.h" #include "..\CommonDeclare\Convert.h" #include <vector> #include <iostream> using std::vector; using std::cout; using std::endl; void MockEmployee(vector<CEmployee*> *pvce) { CCommonEmployee *pZhangSan = new CCommonEmployee(); pZhangSan->SetJob("編寫Java程式,絕對的藍領、苦工加搬運工"); pZhangSan->SetName("張三"); pZhangSan->SetSalary(1800); pZhangSan->SetSex(CEmployee::MALE); pvce->push_back(pZhangSan); CCommonEmployee *pLiSi = new CCommonEmployee(); pLiSi->SetJob("頁面美工,審美素質太不流行了!"); pLiSi->SetName("李四"); pLiSi->SetSalary(1900); pLiSi->SetSex(CEmployee::FEMALE); pvce->push_back(pLiSi); CManager *pWangWu = new CManager(); pWangWu->SetPerformance("基本上是負值,但是我會拍馬屁呀"); pWangWu->SetName("王五"); pWangWu->SetSalary(1900); pWangWu->SetSex(CEmployee::FEMALE); pvce->push_back(pWangWu); } void DoIt() { vector<CEmployee*> vce; MockEmployee(&vce); vector<CEmployee*>::const_iterator readIt = vce.begin(); CBaseVisitor visitor; for (; readIt != vce.end(); readIt ++) { (*readIt)->Accept(&visitor); } cout << "本公司的月工資總額是:" << CConvert::ToString(visitor.GetTotalSalary()).c_str() << endl; vector<CEmployee*>::reverse_iterator delIt = vce.rbegin(); for (; delIt != vce.rend(); delIt++) { delete (*delIt); (*delIt) = NULL; } vce.clear(); }
Main.cpp
main(int argc, _TCHAR* argv[])
{
DoIt();
return 0;
}
Visitor模式優缺點總結:
Visitor模式優點:
(1).實現簡單
Visitor模式缺點:
(1).破壞了封裝性。Visitor模式要求Visitor可以從外部修改Element物件的狀態,這一般通過兩個方式來實現:a)Element提供足夠的public介面,使得Visitor可以通過呼叫這些介面達到修改Element狀態的目的;b)Element暴露更多的細節給Visitor,或者讓Element提供public的實現給Visitor(當然也給了系統中其他的物件),或者將Visitor宣告為Element的friend類,僅將細節暴露給Visitor。但是無論那種情況,特別是後者都將是破壞了封裝性原則(實際上就是C++的friend機制得到了很多的面向物件專家的詬病)。
(2).ConcreteElement的擴充套件很困難:每增加一個Element的子類,就要修改Visitor的介面,使得可以提供給這個新增加的子類的訪問機制。從上面我們可以看到,或者增加一個用於處理新增類的Visit()介面,或者過載一個處理新增類的Visit()操作,或者要修改RTTI方式實現的Visit()實現。無論那種方式都給擴充套件新的Element子類帶來了困難。