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:一个定义了一系列操作的訪问者(基类及其子类) 訪问者模式是一种行为型设计模式 ...
随机推荐
- Python实现图片转文字并翻译至剪切板
一.环境搭建: 1.PySimpleGUI: pip3 install pysimplegui 2.pytesseract需要有tesseract环境才行: 1. 先搭建tesseract: brew ...
- 【BZOJ 3659】 3659: Which Dreamed It (Matrix-Tree&BEST theorem )
3659: Which Dreamed It Time Limit: 20 Sec Memory Limit: 1024 MBSubmit: 134 Solved: 41 Description ...
- 【BZOJ 2054】 2054: 疯狂的馒头 (并查集特技)
Input 第一行四个正整数N,M,p,q Output 一共输出N行,第i行表示第i个馒头的最终颜色(如果最终颜色是白色就输出0). Sample Input 4 3 2 4 Sample Outp ...
- SQL Server附加数据库提示“版本为661,无法打开,支持655版本……”
在我们使用别人导出的数据库的时候,有时候我们会通过附加数据库的方法,把别人导出的数据库附加到我们的电脑中,这时,或许你会遇到这种问题,附加时,提示版本为XXX,无法打开,支持AAA版本. 这是怎么回事 ...
- android aar jar
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha aar 是 安卓 类库项目的 二进制发行包. 文件扩展名 是 aar 专家 mave ...
- luogu P4115 Qtree4
题目链接 luogu P4115 Qtree4 题解 动态点分治,和上一题一样.同样三个堆.就是带权,用边权替换深度就好 为什么要单独写这个题解呢,因为我卡常卡了一天....据说树剖比rmq快? 在第 ...
- 51nod 1962区间计数(单调栈加二分)
题目要求是求出两个序列中处于相同位置区间并且最大值相同的区间个数,我们最直观的感受就是求出每个区间的最大值,这个可以O(N)的求,利用单调栈求出每个数作为最大值能够覆盖的区间. 然后我们可以在进行单调 ...
- POJ3233 Matrix Power Series 矩阵乘法
http://poj.org/problem?id=3233 挺有意思的..学习到结构体作为变量的转移, 题意 : 给定矩阵A,求A + A^2 + A^3 + ... + A^k的结果(两个矩阵相加 ...
- JZYZOJ1330 土地购买 dp 斜率优化
不用long long的话只能ac一半的点而且完全查不出来错...放弃cin保平安.. x[i],y[i]分别为第i块土地的长和宽,输入后需要排序然后去掉冗余数据,最后得到的x[i]递增y[i]递 ...
- [CodeForces-763C]Timofey and remoduling
题目大意: 告诉你一个长度为n的等差数列在模m意义下的乱序值(互不相等),问是否真的存在满足条件的等差数列,并尝试构造任意一个这样的数列. 思路: 首先我们可以有一个结论: 两个等差数列相等,当且仅当 ...