JAVA设计模式之 訪问者模式【Visitor Pattern】
一、概述
訪问者模式是一种较为复杂的行为型设计模式,它包括訪问者和被訪问元素两个主要组成部分。这些被訪问的元素通常具有不同的类型,且不同的訪问者能够对它们进行不同的訪问操作。在使用訪问者模式时,被訪问元素通常不是单独存在的,它们存储在一个集合中。这个集合被称为“对象结构”,訪问者通过遍历对象结构实现对当中存储的元素的逐个操作。訪问者模式是一种对象行为型模式。
二、适用场景
当有多种类型的訪问者(或是操作者) 对一组被訪问者对象集合(或是对象结构)进行操作(当中对象集合也包括多种类型对象),不同的訪问者类型对每一种详细的被訪问者对象提供不同的訪问操作,每种訪问者类型对象对不同的被訪问者也有不同的訪问操作。那么这样的场景就很适用訪问者模式。
假设前面这几句比較绕的文字说明没看明确。那么小吕就举例一个生活中的业务场景:
你所在的公司每一个月人力资源部要对全部员工进行上班时长、加班时长统计。而財务部要对全部员工进行工资核算。不同职位的员工薪资核算标准肯定不一样啊,这个大家都明确。在这个案例中人力资源部和財务部是两个不同类型的部门(訪问者),全部员工(被訪问者)是一个对象集合,而员工又划分为管理者和技术者两种类型(备注:这里小吕仅仅是简单划分为两类),在每月的统计中,人力资源部须要分别对员工进行上班时长和加班时长进行统计。而財务部须要对不同职位的员工进行薪资核算,可见不同部门职责不同,及对员工的訪问操作不同、每一个部门对不同类型的员工的訪问操作也不同。
那么针对这样的场景
我们有必要了解一下訪问者模式。
三、UML类图
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFuaWNlMDUyOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
四、參与者
1>、Visitor(抽象訪问者):为每种详细的被訪问者(ConcreteElement)声明一个訪问操作;
2>、ConcreteVisitor(详细訪问者):实现对被訪问者(ConcreteElement)的详细訪问操作;
3>、Element(抽象被訪问者):通常有一个Accept方法。用来接收/引用一个抽象訪问者对象;
4>、ConcreteElement(详细被訪问者对象):实现Accept抽象方法。通过传入的详细訪问者參数、调用详细訪问者对该对象的訪问操作方法实现訪问逻辑;
5>、Clent、ObjectStructure(client訪问过程測试环境):该过程中,被訪问者通常为一个集合对象,通过对集合的遍历完毕訪问者对每个被訪问元素的訪问操作;
五、用例学习
1.抽象被訪问者:公司员工抽象类 Employee.java
/**
* 公司员工(被訪问者)抽象类
* @author lvzb.software@qq.com
*
*/
public abstract class Employee { /**
* 接收/引用一个抽象訪问者对象
* @param department 抽象訪问者 这里指的是公司部门如 人力资源部、財务部
*/
public abstract void accept(Department department); }
2.详细被訪问者:公司管理岗位员工类 ManagerEmployee.java
/**
* 公司员工:管理者(详细的被訪问者对象)
* @author lvzb.software@qq.com
*
*/
public class ManagerEmployee extends Employee {
// 员工姓名
private String name;
// 每天上班时长
private int timeSheet;
// 每月工资
private double wage;
// 请假/迟到 惩处时长
private int punishmentTime; public ManagerEmployee(String name, int timeSheet, double wage, int punishmentTime) {
this.name = name;
this.timeSheet = timeSheet;
this.wage = wage;
this.punishmentTime = punishmentTime;
} @Override
public void accept(Department department) {
department.visit(this);
} /**
* 获取每月的上班实际时长 = 每天上班时长 * 每月上班天数 - 惩处时长
* @return
*/
public int getTotalTimeSheet(){
return timeSheet * 22 - punishmentTime;
} /**
* 获取每月实际应发工资 = 每月固定工资 - 惩处时长 * 5<br/>
* <作为公司管理者 每迟到1小时 扣5块钱>
* @return
*/
public double getTotalWage(){
return wage - punishmentTime * 5;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public double getWage() {
return wage;
} public void setWage(double wage) {
this.wage = wage;
} public int getPunishmentTime() {
return punishmentTime;
} public void setPunishmentTime(int punishmentTime) {
this.punishmentTime = punishmentTime;
} }
3.详细被訪问者:公司普通岗位员工类 GeneralEmployee.java
/**
* 公司普通员工(详细的被訪问者对象)
* @author lvzb.software@qq.com
*
*/
public class GeneralEmployee extends Employee {
// 员工姓名
private String name;
// 每天上班时长
private int timeSheet;
// 每月工资
private double wage;
// 请假/迟到 惩处时长
private int punishmentTime; public GeneralEmployee(String name, int timeSheet, double wage, int punishmentTime) {
this.name = name;
this.timeSheet = timeSheet;
this.wage = wage;
this.punishmentTime = punishmentTime;
} @Override
public void accept(Department department) {
department.visit(this);
} /**
* 获取每月的上班实际时长 = 每天上班时长 * 每月上班天数 - 惩处时长
* @return
*/
public int getTotalTimeSheet() {
return timeSheet * 22 - punishmentTime;
} /**
* 获取每月实际应发工资 = 每月固定工资 - 惩处时长 * 10<br/>
* <作为公司普通员工 每迟到1小时 扣10块钱 坑吧? 哈哈>
*
* @return
*/
public double getTotalWage() {
return wage - punishmentTime * 10;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public double getWage() {
return wage;
} public void setWage(double wage) {
this.wage = wage;
} public int getPunishmentTime() {
return punishmentTime;
} public void setPunishmentTime(int punishmentTime) {
this.punishmentTime = punishmentTime;
} }
4.抽象訪问者:公司部门抽象类 Department.java
/**
* 公司部门(訪问者)抽象类
* @author lvzb.software@qq.com
*
*/
public abstract class Department { // 声明一组重载的訪问方法,用于訪问不同类型的详细元素(这里指的是不同的员工) /**
* 抽象方法 訪问公司管理者对象<br/>
* 详细訪问对象的什么 就由详细的訪问者子类(这里指的是不同的详细部门)去实现
* @param me
*/
public abstract void visit(ManagerEmployee me); /**
* 抽象方法 訪问公司普通员工对象<br/>
* 详细訪问对象的什么 就由详细的訪问者子类(这里指的是不同的详细部门)去实现
* @param ge
*/
public abstract void visit(GeneralEmployee ge); }
5.详细訪问者:公司財务部类 FADepartment.java
/**
* 详细訪问者对象:公司財务部<br/>
* 財务部的职责就是负责统计核算员工的工资
* @author lvzb.software@qq.com
*
*/
public class FADepartment extends Department { /**
* 訪问公司管理者对象的每月工资
*/
@Override
public void visit(ManagerEmployee me) {
double totalWage = me.getTotalWage();
System.out.println("管理者: " + me.getName() +
" 固定工资 =" + me.getWage() +
", 迟到时长 " + me.getPunishmentTime() + "小时"+
", 实发工资="+totalWage);
} /**
* 訪问公司普通员工对象的每月工资
*/
@Override
public void visit(GeneralEmployee ge) {
double totalWage = ge.getTotalWage();
System.out.println("普通员工: " + ge.getName() +
" 固定工资 =" + ge.getWage() +
", 迟到时长 " + ge.getPunishmentTime() + "小时"+
", 实发工资="+totalWage);
} }
6.详细訪问者:公司人力资源部类 HRDepartment.java
/**
* 详细訪问者对象:公司人力资源部<br/>
* 人力资源部的职责就是负责统计核算员工的每月上班时长
* @author lvzb.software@qq.com
*
*/
public class HRDepartment extends Department { /**
* 訪问公司管理者对象的每月实际上班时长统计
*/
@Override
public void visit(ManagerEmployee me) {
me.getTotalTimeSheet();
} /**
* 訪问公司普通员工对象的每月实际上班时长统计
*/
@Override
public void visit(GeneralEmployee ge) {
ge.getTotalTimeSheet();
} }
7.client測试类:模拟財务部对公司员工的工资核算和訪问 Client.java
import java.util.ArrayList;
import java.util.List; public class Client { public static void main(String[] args) {
List<Employee> employeeList = new ArrayList<Employee>();
Employee mep1,mep2,gep1,gep2,gep3;
// 管理者1
mep1 = new ManagerEmployee("王总", 8, 20000, 10);
// 管理者2
mep2 = new ManagerEmployee("谢经理", 8, 15000, 15);
// 普通员工1
gep1 = new GeneralEmployee("小杰", 8, 8000, 8);
// 普通员工2
gep2 = new GeneralEmployee("小晓", 8, 8500, 12);
// 普通员工3
gep3 = new GeneralEmployee("小虎", 8, 7500, 0); employeeList.add(mep1);
employeeList.add(mep2);
employeeList.add(gep1);
employeeList.add(gep2);
employeeList.add(gep3); // 財务部 对公司员工的工资核算/訪问
FADepartment department = new FADepartment();
for(Employee employee : employeeList){
employee.accept(department);
}
} }
假设要更改为人力资源部对员工的一个月的上班时长统计 则仅仅要将上述代码中的
FADepartment department = new FADepartment();
改动为例如以下就可以
HRDepartment department = new HRDepartment();
8.程序执行结果:
管理者: 王总 固定工资 =20000.0, 迟到时长 10小时, 实发工资=19950.0
管理者: 谢经理 固定工资 =15000.0, 迟到时长 15小时, 实发工资=14925.0
普通员工: 小杰 固定工资 =8000.0, 迟到时长 8小时, 实发工资=7920.0
普通员工: 小晓 固定工资 =8500.0, 迟到时长 12小时, 实发工资=8380.0
普通员工: 小虎 固定工资 =7500.0, 迟到时长 0小时, 实发工资=7500.0
六、其它
JAVA设计模式之 訪问者模式【Visitor Pattern】的更多相关文章
- 设计模式之二十四:訪问者模式(Visitor)
訪问者模式: 定义了一个作用于一个类的一些操作,訪问者模式同意在不改变类的前提下添加一些操作. Represent an operation to be performed on the elemen ...
- 设计模式入门之訪问者模式Visitor
//訪问者模式定义:表示一个作用于某对象结构中的各个元素的操作,它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. //从定义上看.这个模式跟装饰模式的定义非常类似(动态地给一个对象加入一 ...
- Java设计模式(三) Visitor(訪问者)模式及多分派场景应用
基本概念 Visitor 封装一些作用于数据结构中的各元素的操作,不同的操作能够借助新的visitor实现.减少了操作间的耦合性 訪问者能够将数据结构和对数据的操作解耦,使得添加对数据结构的操作不须要 ...
- 设计模式之十五:訪问者模式(Visitor Pattern)
訪问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式. 据<大话设计模式>中说算是最复杂也是最难以理解的一种模式了. 定义(源于GoF<De ...
- 《Java设计模式》之訪问者模式
訪问者模式是对象的行为模式.訪问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作须要改动的话,接受这个操作的数据结构则能够保持不变. 分派的概念 变量被声明时的类型叫做变量的静态类 ...
- C++设计模式实现--訪问者(Visitor)模式
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/L_Andy/article/details/36896645 一. 訪问者模式 定义:表示一个作用于 ...
- JAVA设计模式之:訪问者模式
訪问者模式: 一个作用于某对象结构中各元素的操作,使你能够在不改变各元素类数据结构的前提下添加作用于这些元素的新操作. 结构对象是訪问者模式必备条件.且这个结构对象必须存在遍历自身各个对象的方法. 适 ...
- PHP设计模式——訪问者模式
声明:本系列博客參考资料<大话设计模式>,作者程杰. 訪问者模式表示一个作用于某对象结构中的各元素的操作. 它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. UML类图: w ...
- C++ 设计模式 —— 訪问者(Visitor)
訪问者设计模式的实现借助于两个继承体系, (1)elements:一个是被操作的类(基类及其子类) (2)visitors:一个定义了一系列操作的訪问者(基类及其子类) 訪问者模式是一种行为型设计模式 ...
随机推荐
- linux——(5)文件与文件系统的压缩与打包
概念一:常见的压缩文件拓展名 .z compress程序压缩的文件. .gz gzip程序压缩的文件. .bz2 bzip2程序压缩的文件. .tar tar程序打包的数据,并没有压缩过. .tar. ...
- 2017/11/5 Leetcode 日记
2017/11/5 Leetcode 日记 476. Number Complement Given a positive integer, output its complement number. ...
- 选项卡栏控制器(UITabBarController)
选项卡栏控制器管理的每个场景都包含一个UITabBarItem,它包含标题.图像和徽章. 在场景里可以通过tabBarItem属性来获得UITabBarItem的引用.例如:[self.tabBarI ...
- FastReport.Net使用:[23]图表(Chart)控件
图表基本设置 1.拖放一个图表控件到报表设计界面中. 2.右键菜单“编辑”或者双击图表进入图表编辑器 3.将原有的簇状柱状图删除,添加圆环图 4.绑定数据源,并且指定X,Y轴数据. X轴数据为科目名称 ...
- 【BZOJ 3160】 3160: 万径人踪灭 (FFT)
3160: 万径人踪灭 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1440 Solved: 799 Description Input Outp ...
- ddms 安卓录制
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 屏幕录制, 视频录制 保存为 . 打开 .显示在浏览器里. 设备屏幕捕捉 刷 ...
- python3-开发进阶Flask的基础(2)
知识回顾 1.django 和flask区别? 最大的不同就是django请求相关的数据,通过参数一个一个传递过去的,而flask就是先把放在某个地方,然后去取,这个东西叫上下文管理 2.什么是wsg ...
- php操作redis出现不报错就退出
金乐园项目 php操作redis出现不报错就退出 死活找不出原因是因为下面这句话导致: 原因igbinary扩展没有装上 $options[\Redis::OPT_SERIALIZER] = ...
- UESTC 2015dp专题 H 邱老师选妹子 数位dp
邱老师选妹子 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/65 Descr ...
- uva 6957 Hyacinth bfs
Hyacinth Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/problem/viewPro ...