一、概述

訪问者模式是一种较为复杂的行为型设计模式,它包括訪问者和被訪问元素两个主要组成部分。这些被訪问的元素通常具有不同的类型,且不同的訪问者能够对它们进行不同的訪问操作。在使用訪问者模式时,被訪问元素通常不是单独存在的,它们存储在一个集合中。这个集合被称为“对象结构”,訪问者通过遍历对象结构实现对当中存储的元素的逐个操作。訪问者模式是一种对象行为型模式。

二、适用场景

当有多种类型的訪问者(或是操作者) 对一组被訪问者对象集合(或是对象结构)进行操作(当中对象集合也包括多种类型对象),不同的訪问者类型对每一种详细的被訪问者对象提供不同的訪问操作,每种訪问者类型对象对不同的被訪问者也有不同的訪问操作。那么这样的场景就很适用訪问者模式。

假设前面这几句比較绕的文字说明没看明确。那么小吕就举例一个生活中的业务场景:

你所在的公司每一个月人力资源部要对全部员工进行上班时长、加班时长统计。而財务部要对全部员工进行工资核算。不同职位的员工薪资核算标准肯定不一样啊,这个大家都明确。在这个案例中人力资源部和財务部是两个不同类型的部门(訪问者),全部员工(被訪问者)是一个对象集合,而员工又划分为管理者和技术者两种类型(备注:这里小吕仅仅是简单划分为两类),在每月的统计中,人力资源部须要分别对员工进行上班时长和加班时长进行统计。而財务部须要对不同职位的员工进行薪资核算,可见不同部门职责不同,及对员工的訪问操作不同、每一个部门对不同类型的员工的訪问操作也不同。

那么针对这样的场景 
我们有必要了解一下訪问者模式。

三、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】的更多相关文章

  1. 设计模式之二十四:訪问者模式(Visitor)

    訪问者模式: 定义了一个作用于一个类的一些操作,訪问者模式同意在不改变类的前提下添加一些操作. Represent an operation to be performed on the elemen ...

  2. 设计模式入门之訪问者模式Visitor

    //訪问者模式定义:表示一个作用于某对象结构中的各个元素的操作,它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. //从定义上看.这个模式跟装饰模式的定义非常类似(动态地给一个对象加入一 ...

  3. Java设计模式(三) Visitor(訪问者)模式及多分派场景应用

    基本概念 Visitor 封装一些作用于数据结构中的各元素的操作,不同的操作能够借助新的visitor实现.减少了操作间的耦合性 訪问者能够将数据结构和对数据的操作解耦,使得添加对数据结构的操作不须要 ...

  4. 设计模式之十五:訪问者模式(Visitor Pattern)

    訪问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式. 据<大话设计模式>中说算是最复杂也是最难以理解的一种模式了. 定义(源于GoF<De ...

  5. 《Java设计模式》之訪问者模式

    訪问者模式是对象的行为模式.訪问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作须要改动的话,接受这个操作的数据结构则能够保持不变. 分派的概念 变量被声明时的类型叫做变量的静态类 ...

  6. C++设计模式实现--訪问者(Visitor)模式

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/L_Andy/article/details/36896645 一. 訪问者模式 定义:表示一个作用于 ...

  7. JAVA设计模式之:訪问者模式

    訪问者模式: 一个作用于某对象结构中各元素的操作,使你能够在不改变各元素类数据结构的前提下添加作用于这些元素的新操作. 结构对象是訪问者模式必备条件.且这个结构对象必须存在遍历自身各个对象的方法. 适 ...

  8. PHP设计模式——訪问者模式

    声明:本系列博客參考资料<大话设计模式>,作者程杰. 訪问者模式表示一个作用于某对象结构中的各元素的操作. 它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. UML类图: w ...

  9. C++ 设计模式 —— 訪问者(Visitor)

    訪问者设计模式的实现借助于两个继承体系, (1)elements:一个是被操作的类(基类及其子类) (2)visitors:一个定义了一系列操作的訪问者(基类及其子类) 訪问者模式是一种行为型设计模式 ...

随机推荐

  1. Highcharts实现走势图

    Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,并且免费提供给个人学习.个人网站和非商业用途使用.HighCh ...

  2. 【BZOJ 2039】 2039: [2009国家集训队]employ人员雇佣 (最小割)

    2039: [2009国家集训队]employ人员雇佣 Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 1511  Solved: 728 Descri ...

  3. POJ3687 Katu Puzzle

    好好写2-sat 如果a1-->b1矛盾则连边a1-->b2和b1-->a2 我定了一个ccnt和cnt变量,结果少打一个c,wa了好多次(lll¬ω¬) By:大奕哥 #inclu ...

  4. [BZOJ4555][TJOI2016&HEOI2016]求和(分治FFT)

    4555: [Tjoi2016&Heoi2016]求和 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 525  Solved: 418[Sub ...

  5. Stirling数,Bell数,Catalan数,Bernoulli数

    组合数学的实质还是DP,但是从通式角度处理的话有利于FFT等的实现. 首先推荐$Candy?$的球划分问题集合: http://www.cnblogs.com/candy99/p/6400735.ht ...

  6. POJ1916 Period KMP

    http://poj.org/problem?id=1961 判断某一字符串中 , 哪些前缀子串有周期 , 输出子串长度以及子串中周期重复的次数 ( 次数>1 )  需要的只是对KMP性质的进一 ...

  7. [转]Android网格视图(GridView)

    GridView的一些属性: 1.android:numColumns=”auto_fit”   //GridView的列数设置为自动,也可以设置成2.3.4…… 2.android:columnWi ...

  8. mysql内存参数整理和条调优以及内存统计

    date:20140530auth:Jin 参考:http://dev.mysql.com/doc/refman/5.5/en/server-status-variables.html#http:// ...

  9. 微信小程序导航栏,下面内容滑动,上册导航栏跟着滑动,内容随着导航栏滑动

    16.类似微信导航栏滑动.png 今日头条导航栏,下面滑动上面跟着滑动 index.wxml <swiper class="content" style="heig ...

  10. UVA 11205 The broken pedometer(子集枚举)

    B - The broken pedometer Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu ...