一 、描述
概念:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
其典型的结构图为:
Visitor 模式在不破坏类的前提下,为类提供增加新的新操作。Visitor 模式的关键是双分派(Double-Dispatch)的技术】。C++语言支持的是单分派。
在 Visitor 模式中 Accept()操作是一个双分派的操作。具体调用哪一个具体的 Accept()操作,有两个决定因素:
1)Element 的类型。因为 Accept()是多态的操作,需要具 体的 Element 类型的子类才可以决定到底调用哪一个 Accept()实现;
2)Visitor 的类型。 Accept()操作有一个参数(Visitor* vis),要决定了实际传进来的Visitor的实际类别才可 以决定具体是调用哪个 VisitConcrete()实现。
二、实例:
描述:
今天天气不错,绝对是晴空万里,骄阳似火呀,好,我们今天来讲访问者模式,我们在前面讲了组合模式和迭代器模式,通过组合模式我们能够把一个公司的人员组织机构树搭建起来,给管理带来非常大的便利,通过迭代器模式我们可以把每一个员工都遍历一遍,看看是不是有“人去世了还在领退休金”,“拿高工资而不干活的尸位素餐”等情况,那我们今天的要讲访问者模式是做什么用的呢?
我们公司有七百多技术人员,分布在全国各地,组织架构你在组合模式中也看到了,很常见的家长领导型模式,每个技术人员的岗位都是固定的,你在组织机构在那棵树下,充当的是什么叶子节点都是非常明确的,每一个员工的信息比如名字、性别、薪水等都是记录在数据库中,现在有这样一个需求,我要把公司中的所有人员信息都打印汇报上去,很简单吧,我们来看类图:
这个类图还是比较简单的,使用了一个模版方法模式,把所要的信息都打印出来, 但我们来想一想这个实际的情况,人力资源部门拿这这份表格会给谁看呢?那当然是大老板了,大老板关心的是什么?关心部门经理的业绩!小兵的情况不是他要了解的,就像二战的时候一位将军(巴顿?艾森豪威尔?记不清楚了)说的“我一想到我的士兵也有孩子、妻子、父母,我就痛心疾首,...但是这是战场,我只能认为他们是一群机器...”,是呀,其实我们也一样呀,那问题就出来了:
1)大老板就看部门经理的报表,小兵的报表可看可不看;
2)多个大老板,“嗜好”是不同的,主管销售的,则主要关心的营销情况;主管会计的,则主要关心 企业的整体财务运行状态;主管技术的,则主要看技术的研发情况;综合成一句话,这个报表会有修改:数据的修改以及报表的展现修改,按照开闭原则,项目分析的时候已经考虑到这些可能引起变更的隐私,就需要在设计时考虑通过扩展来来避开未来需求变更而引起的代码修改风险。我们来想一想,每个普通员工类和经理类都一个方法 report,那是否可以把这个方法提取到另外一个类中来实现呢?原有的示意图如下:
这两个类都一个相同的方法 report(),但是要实现的内容不相同,而且还有可能会发生变动,那我们就让其他类来实现这个 report 方法,好,看示意图图的变更:
两个类的 report 方法都不需要了,只有 Visitor 类来实现了 report 的方法,这个猛一看还真有点委托(intergration)的意味,我们实现下来你就知道这和委托有非常大的差距,我们来看类图:(类名稍作变化)
在抽象类 Employee 中增加了 accept 方法,这个方法是定义我这个类可以允许被谁来访问,也就定义一类访问者,在具体的实现类中调用访问者的方法。
我的工程目录:
注释:
main(),客户
IVisitor,访问者接口
CBaseVisitor,访问者实现类
CEmployee,被访问者抽象类(员工)
CCommonEmployee,被访问者实现类之一(普通员工)
CManager,被访问者实现类之二(经理)
Convert,整型转字符型方法
说明:A接受B的访问,B主动的执行访问动作。
注意:和观察者的区别是,被观察者要执行一个动作,然后主动发送通知给观察者。访问者模式是由访问者主动发出的动作。
Employee.h
#ifndef __Visitor__Employee__
#define __Visitor__Employee__
#include <iostream>
#include "IVisitor.h"
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;
};
#endif /* defined(__Visitor__Employee__) */
Employee.cpp
#include "Employee.h"
int CEmployee::MALE = 0; //0代表是男性
int CEmployee::FEMALE = 1; //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;
}
被访问者实现类之一(普通员工):CCommonEmployee类
CCommonEmployee.h
#ifndef __Visitor__CommonEmployee__
#define __Visitor__CommonEmployee__
#include <iostream>
#include "Employee.h"
#include "IVisitor.h"
using std::string;
class CCommonEmployee :
public CEmployee
{
public:
CCommonEmployee(void);
~CCommonEmployee(void);
string GetJob();
void SetJob(string job);
//我允许访问者过来访问
void Accept(IVisitor *pVisitor);
protected:
string GetOtherInfo();
private:
//工作内容,这个非常重要,以后的职业规划就是靠这个了
string m_job;
};
#endif /* defined(__Visitor__CommonEmployee__) */
CommonEmployee.cpp
#include "CommonEmployee.h"
using std::string;
CCommonEmployee::CCommonEmployee(void)
{
this->m_job = "";
}
CCommonEmployee::~CCommonEmployee(void)
{
}
string CCommonEmployee::GetJob()
{
return this->m_job;
}
void CCommonEmployee::SetJob(string job)
{
this->m_job = job;
}
string CCommonEmployee::GetOtherInfo()
{
string job = "";
job.append("工作");
job.append(this->m_job);
job.append("\t");
return job;
}
void CCommonEmployee::Accept(IVisitor *pVisitor)
{
pVisitor->Visit(*this);
}
被访问者实现类之二(经理):CManager
CManager.h
#ifndef __Visitor__Manager__
#define __Visitor__Manager__
#include <iostream>
#include "Employee.h"
#include "IVisitor.h"
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;
};
#endif /* defined(__Visitor__Manager__) */
CManager.cpp
#include "Manager.h"
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);
}
访问者接口:IVisitor
IVisitor.h
#ifndef Visitor_IVisitor_h
#define Visitor_IVisitor_h
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;
};
#endif
访问者实现类:CBaseVisitor
CBaseVisitor.h
#ifndef __Visitor__BaseVisitor__
#define __Visitor__BaseVisitor__
#include <iostream>
#include "IVisitor.h"
#include "CommonEmployee.h"
#include "Manager.h"
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; //部门经理的工资系数是5
static const int COMMONEMPLOYEE_COEFFICENT = 2; //员工的工资系数是2
int m_commonTotal; //普通员工的工资总和
int m_managerTotal; //部门经理的工资总和
void CalCommonSalary(int salary); //计算普通员工的工资总和
void CalManagerSalary(int salary); //计算部门经理的工资总和
};
#endif /* defined(__Visitor__BaseVisitor__) */
CBaseVisitor.cpp
#include "BaseVisitor.h"
#include "Convert.h"
using std::string;
using std::cout;
using std::endl;
CBaseVisitor::CBaseVisitor(void)
{
m_commonTotal = 0;
m_managerTotal = 0;
//MANAGER_COEFFICENT = 5; //部门经理的工资系数是5
//COMMONEMPLOYEE_COEFFICENT = 2; //员工的工资系数是2
}
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;
}
整型转字符型:Convert
Convert.h
#include <iostream>
using std::string;
#ifdef COMMONDECLARE_EXPORTS
#define COMMONDECLARE_API __declspec(dllexport)
#else
#define COMMONDECLARE_API __declspec(dllimport)
#endif
class COMMONDECLARE_API CConvert
{
public:
CConvert(void);
~CConvert(void);
static string ToString(int i);
};
Convert.cpp
#include "Convert.h"
#include <iostream>
#include <sstream>
using std::string;
using std::stringstream;
CConvert::CConvert(void)
{
}
CConvert::~CConvert(void)
{
}
string CConvert::ToString(int i)
{
stringstream ss;
ss << i;
string result = ss.str();
return result;
}
客户:main
main.cpp
#include <iostream>
#include "Employee.h"
#include "CommonEmployee.h"
#include "Manager.h"
#include "BaseVisitor.h"
#include "Convert.h"
#include <vector>
using std::vector;
using std::cout;
using std::endl;
//模拟出公司的人员情况,我们可以想象这个数据室通过持久层传递过来的
void MockEmployee(vector<CEmployee*> *pvce)
{
//产生张三这个员工
CCommonEmployee *pZhangSan = new CCommonEmployee();
pZhangSan->SetJob("编写C++程序,绝对的蓝领、苦工加搬运工");
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();
}
int main(int argc, const char * argv[])
{
DoIt();
// insert code here...
std::cout << "Hello, World!\n";
return 0;
}
结果如下:
参考文献:《设计模式之禅》,《GoF_23种设计模式解析》
参考博客: http://www.cnblogs.com/wanggary/archive/2011/04/21/2024099.html