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

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

  

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

一 访问者模式概述

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) = 0;
};

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) = 0;virtual void Visit(CPartTimeEmployee *pPartTimeEmployee) = 0;
};

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 >= 40){dbWeeklyWage = dbWeeklyWage + (nWeeklyTime - 40) * 50;}else{dbWeeklyWage = dbWeeklyWage - (40 - nWeeklyTime) * 80;if (dbWeeklyWage < 0){dbWeeklyWage = 0;}}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 > 40){cout << "正式员工:"<< strName.c_str()<< " 加班时间为:" << nWeeklyTime-40<< "小时" << endl;}else if (nWeeklyTime < 40){cout << "正式员工:"<< strName.c_str()<< " 请假时间为:" << 40-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, 45);IEmployee *pEmployee2 = new CFullTimeEmployee("李四", 2000, 40);IEmployee *pEmployee3 = new CFullTimeEmployee("王麻子", 2400, 38);IEmployee *pEmployee4 = new CPartTimeEmployee("路人甲", 80, 20);IEmployee *pEmployee5 = new CPartTimeEmployee("路人乙", 60, 18);IEmployee *pEmployee6 = new CPartTimeEmployee("路人丙", 70, 19);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)模式!

转载于:https://www.cnblogs.com/xiaobingqianrui/p/9028707.html

设计模式之访问者(visitor)模式相关推荐

  1. 设计模式学习笔记——访问者(Visitor)模式

    设计模式学习笔记--访问者(Visitor)模式 @(设计模式)[设计模式, 访问者模式, visitor] 设计模式学习笔记访问者Visitor模式 基本介绍 访问者案例 类图 实现代码 Visit ...

  2. 设计模式学习笔记--访问者(Visitor)模式

    写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式:每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案:当我们碰到模式 ...

  3. 【鲁班学院】设计模式—访问者(Visitor)模式

    一.定义 访问者模式是一种从操作的对象结构中分离算法的方式. 它可以在不改变数据结构的前提下定义作用与这些元素的新操作.它遵循开闭原则. Represent an operation to be pe ...

  4. 设计模式--访问器(Visitor)模式

    模式定义 表示一个作用于某对象结构中的各元素的操作,使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化) 类图 要点总结 Visitor模式通过所谓双重分发(doubl ...

  5. 小白设计模式:访问者模式

    定义 可作用于对象结构中各个元素,在不改变各元素类的前提下,定义作用于这些元素新操作方法的一种行为型设计模式. 主要组成 抽象访问者(Visitor): 声明出对对象结构中每一个具体元素的访问方法vi ...

  6. java visitor_Java 的双重分发与 Visitor 模式

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

  7. 设计模式之访问者模式(Visitor)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

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

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

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

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

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

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

最新文章

  1. sar sensor传感器的作用_传感器攻防战-惯导IMU
  2. 左神算法:调整搜索二叉树中两个错误的节点(Java版)
  3. TZOJ 3030 Courses(二分图匹配)
  4. 【POJ】3268 Silver Cow Party
  5. 两个月新手的几点 storyboard 心得
  6. Myeclipse学习总结(1)——Myeclipse优化配置
  7. linux和windows下icmp的区别
  8. 4. CSS id 选择器
  9. java和C#的区别汇总
  10. HDU 1527 取石子游戏
  11. Maven中的DependencyManagement和Dependencies
  12. 基于java的试题库管理系统(java CS窗体版)
  13. Ubuntu 18.04安装i686-elf交叉编译工具链的方法
  14. typedef struct Lnode{ Elemtype data; struct Lnode *next; } Lnode,*Linklist;
  15. 判断一个数是否为质数(素数)
  16. 〖小狼毫〗小狼毫使用心得分享
  17. 金玉良缘易配而木石前盟难得|M1 Mac os(Apple Silicon)天生一对Python3开发环境搭建(集成深度学习框架Tensorflow/Pytorch)
  18. C程序设计(第五版)谭浩强第七章课后答案
  19. 新增spring Converter解析器中使用lambda表达式代替匿名内部类是启动报错:... does the class parameterize those types?
  20. 暨南大学21考研计算机上岸吐槽贴经验贴流水账

热门文章

  1. 计算机科学与技术python方向是什么意思-2020年西京学院计算机科学与技术专业专业介绍...
  2. 不懂编程可以自学python吗-python教程看完了,还是不会编程?
  3. python怎么写文件-Python 读写文件
  4. python面试-Python面试技巧合集(建议收藏)
  5. python 仪表盘-跟小白学Python数据分析——绘制仪表盘
  6. python画图颜色-python中matplotlib的颜色及线条控制的示例
  7. python可以做什么开发-python适合做什么开发_python未来发展怎么样
  8. python画长方形-怎么用python 画出任意占空比的一串矩形方波呢?
  9. python中的装饰器有哪些-python 装饰器以及开发中常用的例子
  10. 软件测试用python一般用来做什么-如何将Python应用到实际测试工作中?