在患者就医时,医生会根据病情开具处方单,很多医院都会存在以下这个流程:划价人员拿到处方单之后根据药品名称和数量计算总价,而药房工作人员根据药品名称和数量准备药品,如下图所示。

在软件开发中,有时候也需要处理像处方单这样的集合对象结构,在该对象结构中存储了多个不同类型的对象信息,而且对同一对象结构中的元素的操作方式并不唯一,可能需要提供多种不同的处理方式。在设计模式中,有一种模式可以满足上述要求,其模式动机就是以不同的方式操作复杂对象结构,该模式就是访问者模式。

  

  访问者模式是一个可以考虑用来解决的方案,它可以在一定程度上解决上述问题(大部分问题)。

一 访问者模式概述

1.1 访问者模式简介

  访问者模式是一种较为复杂的行为型模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操作。例如:处方单中的各种药品信息就是被访问的元素,而划价人员和药房工作人员就是访问者。访问者模式可以使得用户在不修改现有系统的情况下扩展系统的功能,为这些不同类型的元素增加新的操作。

访问者(Visitor)模式:提供一个作用于某对象结构中的各元素的操作表示,它使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。

1.2 需求背景

Background:M公司开发部想要为某企业开发一个OA系统,在该OA系统中包含一个员工信息管理子系统,该企业包括正式员工和临时工,每周HR部门和财务部等部门需要对员工数据进行汇总,汇总数据包括员工工作时间、员工工资等等。该企业的基本制度如下:

(1)正式员工(Full time Employee)每周工作时间为40小时,不同级别、不同部门的员工每周基本工资不同;如果超过40小时,超出部分按照100元/小时作为加班费;如果少于40小时,所缺时间按照请假处理,请假锁扣工资以80元/小时计算,直到基本工资扣除到0为止。除了记录实际工作时间外,HR部需要记录加班时长或请假时长,作为员工平时表现的一项依据。

(2)临时员工(Part time Employee)每周工作时间不固定,基本工资按照小时计算,不同岗位的临时工小时工资不同。HR部只需要记录实际工作时间。

HR人力资源部和财务部工作人员可以根据各自的需要对员工数据进行汇总处理,HR人力资源部负责汇总每周员工工作时间,而财务部负责计算每周员工工资。

1.3 类图

  

1.4 代码实现

1.4.1 抽象员工类

// 被雇佣者类
#pragma once #include <string>
using namespace std; class IDepartment;
// 抽象雇佣者类
class IEmployee
{
public:
IEmployee(){}
public:
virtual ~IEmployee(){}
virtual void Accept(IDepartment *pDepartment) = ;
};

1.4.2 全职员工类

class CFullTimeEmployee : public IEmployee
{
public:
CFullTimeEmployee();
CFullTimeEmployee(string strName, double dbWeeklyWage, int nWeeklyTime);
~CFullTimeEmployee(); void Accept(IDepartment *pDepartment);
string GetName()
{
return m_strName;
} double GetWeeklyWage()
{
return m_dbWeeklyWage;
} int GetWeeklyTime()
{
return m_nWeeklyTime;
} private:
string m_strName;
double m_dbWeeklyWage;
int m_nWeeklyTime;
};
CFullTimeEmployee::CFullTimeEmployee()
{ } CFullTimeEmployee::CFullTimeEmployee(string strName, double dbWeeklyWage, int nWeeklyTime)
:m_strName(strName),m_dbWeeklyWage(dbWeeklyWage),m_nWeeklyTime(nWeeklyTime)
{
} CFullTimeEmployee::~CFullTimeEmployee()
{ } void CFullTimeEmployee::Accept(IDepartment *pDepartment)
{
pDepartment->Visit(this);
}

1.4.3 临时员工类

class CPartTimeEmployee : public IEmployee
{
public:
CPartTimeEmployee();
CPartTimeEmployee(string strName, double dbWeeklyWage, int nWeeklyTime);
~CPartTimeEmployee();
void Accept(IDepartment *pDepartment);
string GetName()
{
return m_strName;
} double GetWeeklyWage()
{
return m_dbWeeklyWage;
} int GetWeeklyTime()
{
return m_nWeeklyTime;
} private:
string m_strName;
double m_dbWeeklyWage;
int m_nWeeklyTime;
};
CPartTimeEmployee::CPartTimeEmployee()
{ }
CPartTimeEmployee::CPartTimeEmployee(string strName, double dbWeeklyWage, int nWeeklyTime)
:m_strName(strName),m_dbWeeklyWage(dbWeeklyWage),m_nWeeklyTime(nWeeklyTime)
{
}
CPartTimeEmployee::~CPartTimeEmployee()
{
} void CPartTimeEmployee::Accept(IDepartment *pDepartment)
{
pDepartment->Visit(this);
}

1.4.4 抽象部门类

// 访问者类
#pragma once
#include "employee.h" #include <string>
#include <iostream>
using namespace std; class IDepartment
{
protected:
IDepartment(){}
public:
virtual ~IDepartment(){}
virtual void Visit(CFullTimeEmployee *pFullTimeEmployee) = ;
virtual void Visit(CPartTimeEmployee *pPartTimeEmployee) = ;
};

1.4.5 财务部门类

class CFinanceDepartment : public IDepartment
{
public:
CFinanceDepartment(){}
~CFinanceDepartment(){} void Visit(CFullTimeEmployee *pFullTimeEmployee)
{
string strName = pFullTimeEmployee->GetName();
double dbWeeklyWage = pFullTimeEmployee->GetWeeklyWage();
int nWeeklyTime = pFullTimeEmployee->GetWeeklyTime(); if (nWeeklyTime >= )
{
dbWeeklyWage = dbWeeklyWage + (nWeeklyTime - ) * ;
}
else
{
dbWeeklyWage = dbWeeklyWage - ( - nWeeklyTime) * ;
if (dbWeeklyWage < )
{
dbWeeklyWage = ;
}
} cout << "正式员工:"<< strName.c_str()<< " 实际工资为:" << dbWeeklyWage << "元" << endl;
} void Visit(CPartTimeEmployee *pPartTimeEmployee)
{
string strName = pPartTimeEmployee->GetName();
double dbWeeklyWage = pPartTimeEmployee->GetWeeklyWage();
int nWeeklyTime = pPartTimeEmployee->GetWeeklyTime(); cout << "临时员工:"<< strName.c_str()<< " 实际工资为:" << dbWeeklyWage *nWeeklyTime<< "元" << endl;
}
};

1.4.6 人力资源部门类

class CHrDepartment : public IDepartment
{
public:
CHrDepartment(){}
~CHrDepartment(){} void Visit(CFullTimeEmployee *pFullTimeEmployee)
{
string strName = pFullTimeEmployee->GetName();
int nWeeklyTime = pFullTimeEmployee->GetWeeklyTime(); if (nWeeklyTime > )
{
cout << "正式员工:"<< strName.c_str()<< " 加班时间为:" << nWeeklyTime-<< "小时" << endl;
}
else if (nWeeklyTime < )
{
cout << "正式员工:"<< strName.c_str()<< " 请假时间为:" << -nWeeklyTime<< "小时" << endl;
}
} void Visit(CPartTimeEmployee *pPartTimeEmployee)
{
string strName = pPartTimeEmployee->GetName();
int nWeeklyTime = pPartTimeEmployee->GetWeeklyTime(); cout << "临时员工:"<< strName.c_str()<< " 上班时间为:" << nWeeklyTime<< "小时" << endl;
}
};

1.4.6 员工信息管理类

#pragma once

#include <vector>
using namespace std; #include "employee.h"
#include "department.h" // 被访问者管理类
class CEmployeeMgr
{
public:
CEmployeeMgr(){}
~CEmployeeMgr()
{
vector<IEmployee *>::iterator iter;
for (iter = m_EmployeeVect.begin(); iter != m_EmployeeVect.end();iter ++)
{
delete *iter;
}
} void Add(IEmployee *pEmployee)
{
m_EmployeeVect.push_back(pEmployee);
} void Accept(IDepartment *pDepartment)
{
vector<IEmployee *>::iterator iter;
for (iter = m_EmployeeVect.begin(); iter != m_EmployeeVect.end();iter ++)
{
(*iter)->Accept(pDepartment);
}
} private:
vector<IEmployee *> m_EmployeeVect;
};

1.5 测试

#include "stdio.h"

#include "employeemgr.h"

void main()
{
CEmployeeMgr *pEmployeeMgr = new CEmployeeMgr(); IEmployee *pEmployee1 = new CFullTimeEmployee("张三", 3200.00, );
IEmployee *pEmployee2 = new CFullTimeEmployee("李四", , );
IEmployee *pEmployee3 = new CFullTimeEmployee("王麻子", , );
IEmployee *pEmployee4 = new CPartTimeEmployee("路人甲", , );
IEmployee *pEmployee5 = new CPartTimeEmployee("路人乙", , );
IEmployee *pEmployee6 = new CPartTimeEmployee("路人丙", , ); pEmployeeMgr->Add(pEmployee1);
pEmployeeMgr->Add(pEmployee2);
pEmployeeMgr->Add(pEmployee3);
pEmployeeMgr->Add(pEmployee4);
pEmployeeMgr->Add(pEmployee5);
pEmployeeMgr->Add(pEmployee6); IDepartment *pFinanceDepartment = new CFinanceDepartment();
IDepartment *pHrDepartment = new CHrDepartment(); pEmployeeMgr->Accept(pFinanceDepartment);
//pEmployeeMgr->Accept(pHrDepartment); delete pFinanceDepartment;
delete pHrDepartment;
delete pEmployeeMgr;
return;
}

二 访问者模式总结

2.1 主要优点

  (1)增加新的访问操作十分方便,不痛不痒 => 符合开闭原则

  (2)将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中,类的职责更加清晰 => 符合单一职责原则

2.2 主要缺点

  (1)增加新的元素类很困难,需要在每一个访问者类中增加相应访问操作代码 => 违背了开闭原则

  (2)元素对象有时候必须暴露一些自己的内部操作和状态,否则无法供访问者访问 => 破坏了元素的封装性

2.3 应用场景

  (1)一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。=> 不同的类型可以有不同的访问操作

  (2)对象结构中对象对应的类很少改变 很少改变 很少改变(重要的事情说三遍),但经常需要在此对象结构上定义新的操作。

注:本文中的图片和文字来源于原博设计模式的征途—16.访问者(Visitor)模式

设计模式之访问者(visitor)模式的更多相关文章

  1. 设计模式-访问者(Visitor)模式

    访问者模式是对象的行为模式.访问者模式的目的是封装施加在某种数据结构元素上的操作.一旦一些操作需要修改,接受这个操作的数据结构可以保持不变. 个人觉得访问者模式相对其他的设计模式来说稍微复杂,难理解一 ...

  2. [设计模式]访问者 Visitor 模式

    访问者模式是对象的行为模式. 访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变.

  3. 设计模式C++描述----22.访问者(Visitor)模式

    一. 访问者模式 定义:表示一个作用于某对象结构中的各元素的操作.它你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 结构如下: 二. 举例 假设有一项科学实验,是用来对比两种种子在不同环 ...

  4. Java 的双重分发与 Visitor 模式

    双重分发(Double Dispatch) 什么是双重分发? 谈起面向对象的程序设计时,常说起的面向对象的「多态」,其中关于多态,经常有一个说法是「父类引用指向子类对象」. 这种父类的引用指向子类对象 ...

  5. [设计模式] 23 访问者模式 visitor Pattern

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问 ...

  6. 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)

    原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...

  7. 设计模式(17) 访问者模式(VISITOR) C++实现

    意图: 表示一个作用于某对象结构的各元素的操作.它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作. 动机: 之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式. 在静态 ...

  8. 设计模式23:Visitor 访问者模式(行为型模式)

    Visitor 访问者模式(行为型模式) 动机(Motivation)在软件构造过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的修改,将会给子类带来繁重的 ...

  9. 设计模式:访问者(Visitor)模式

    设计模式:访问者(Visitor)模式 一.前言    什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+ ...

  10. Visitor模式(访问者设计模式)

    Visitor ? 在Visitor模式中,数据结构与处理被分离开来.我们编写一个表示"访问者"的类来访问数据结构中的元素, 并把对各元素的处理交给访问者类.这样,当需要增加新的处 ...

随机推荐

  1. Zabbix Linux http 监控脚本

    说明:自定义监控脚本,监控内存是否启用主进程 创建文件:vim check_http.sh #!/bin/bash result=`ps -ef | grep httpd | grep -v grep ...

  2. 20145222黄亚奇《网络对抗》- shellcode注入&Return-to-libc攻击深入

    20145222黄亚奇<网络对抗>- shellcode注入&Return-to-libc攻击深入 shellcode注入实践过程

  3. 通过excel快速拼接SQL

    “哎,发你一个excel,把这几百条数据修复喽.”经理喊道. “嗯,好的!” 正在看资料的我被经理临时分的任务打断,搞吧!这就是我平时中的一个工作场景. 工作中总是会遇到要修复数据,数据在excel中 ...

  4. some words

    For we meet in an hour of change and challenge,              in a dacade of hope and fear,   in an a ...

  5. java中泛型的一个小问题

    最近做项目,由于java语法不是非常的熟悉,编写代码过程中不难遇到一些问题: 代码里写了一条这种语句: Map<String, List<String>> configFile ...

  6. Define class with itself as generic implementation. Why/how does this work?

    https://stackoverflow.com/questions/10709061/define-class-with-itself-as-generic-implementation-why- ...

  7. 基于XML配置的AOP实现日志打印

    Spring中可以使用注解或XML文件配置的方式实现AOP.1.导入jar包 com.springsource.net.sf.cglib -2.2.0.jar com.springsource.org ...

  8. 调用webservices中 枚举类型没有被序列化问题

    引用服务后,代理类为自动为所有枚举类型生成了一个Bool类型相关字段,命名方式:比如枚举类名为“PayType”,生成的相关字段为“PayTypeSpecified”,此字段有何作用? PayType ...

  9. Scala的两种变量

    Scala有两种变量,val和var.val类似于Java的final变量,一旦初始化了,就不能再赋值了.var如同Java中的非final变量,可以在生命周期内被多次赋值.

  10. JAVA中的数据存储(堆及堆栈)- 转载

    1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可 ...