1. 程式人生 > >Visitor模式詳解--設計模式(20)

Visitor模式詳解--設計模式(20)

Visitor模式來源:

 在面向物件系統的開發和設計過程,經常會遇到一種情況就是需求變更(RequirementChanging),經常我們做好的一個設計、實現了一個系統原型,咱們的客戶又會有了新的需求。我們又因此不得不去修改已有的設計,最常見就是解決方案就是給已經設計、實現好的類新增新的方法去實現客戶新的需求,這樣就陷入了設計變更的夢魘:不停地打補丁,其帶來的後果就是設計根本就不可能封閉、編譯永遠都是整個系統程式碼。Visitor模式則提供了一種解決方案。

Visitor模式作用

  將更新(變更)封裝到一個類中(訪問操作),並由待更改類提供一個接收介面,則可達到效果。

Visitor

模式UML結構圖如圖1所示

                             

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子類帶來了困難。