我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现访问者模式(设计模式)。

不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。

我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个访问者模式,真实的实现一个,你看懂代码后,自然就明白了。

访问者模式Visitor Pattern(行为型设计模式)

定义:表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。

我认为一个对访问者模式所体现的设计思路最简单的理解就是:仁者见仁,智者见智。我们每一个人对同一个事物的看法是不太一样的,对同一个问题我们每个人会产生完全不同的想法,而访问者模式就是在模拟这种情况,即对同一个数据,不同类的对象有不同的处理方法。换句话说,在模拟这种场景。

再另外举一个例子,相信大家在实现手机交电费以前,总是和一个叫电费单的东西打交道。如下:

在电力公司开具电费单后,电力公司财务人员拿到单之后根据用电情况计算总价,片区管理工作人员根据上面内容通知用户收取电费,而用户则要根据电费单准备好钱进行交费。

可以将电费单看成一个信息的集合,里面包含了一种或多种不同类型的信息,不同类型的人员在操作同一个信息集合时将提供不同的处理方式,而且可能还会增加新类型的人员来操作电费单。

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

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

在使用访问者模式时,被访问元素通常不是单独存在的,它们存储在一个集合中,这个集合被称为“对象结构”,访问者通过遍历对象结构实现对其中存储的元素的挨个操作。其结构如下图:

访问者模式包含Visitor(抽象访问者)、ConcreteVisitor(具体访问者)、Element(抽象元素)、ConcreteElement(具体元素)、ObjectStructure(对象结构)等5个角色。抽象访问者为对象结构中的每一个具体元素类声明一个操作,从这个操作的名称或参数类型可以清楚地知道需要访问的具体元素的类型,具体访问者需要实现这些操作方法,定义对这些元素的访问操作。具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素。抽象元素一般是抽象类或者接口,它声明了一个方法用于接受访问者的访问操作。具体元素实现了 方法,在方法中调用访问者的访问方法以便完成对一个元素的操作。对象结构是一个元素的集合,它用于存放元素对象并且提供了遍历其内部元素的方法。

在访问者模式中,在访问者模式中增加新的访问者无须修改原有系统,系统具有较好的可扩展性。对象结构存储了不同类型的元素对象,以供不同访问者访问。访问者模式包括两个层次结构:一个是访问者层次结构,提供了抽象访问者和具体访问者;一个是元素层次结构,提供了抽象元素和具体元素。相同的访问者可以用不同的方式访问不同的元素.相同的元素可以接受不同访问者以不同的方式访问。

JAVA代码实现

public abstract class Visitor {public abstract void visit(ConcreteElementA elementA);public abstract void visit(ConcreteElementB elementB);public void visit(ConcreteElementC elementC){//元素ConcreteElementC操作代码}
}public class Concretevisitor extends Visitor {public void visit(ConcreteElementA elementA){//元素ConcreteElementA操作代码}public void visit(ConcreteElementB elementB){//元素ConcreteElementB操作代码}
}public interface Element{public void accept(Visitor visitor);
}public class ConcreteElementA implements Element {public void accept(Visitor visitor){visitor.visit(this);}public void operationA()(//业务方法}
}public class ObjectStructure{//定义一个集合用于存储元素对象  private ArrayList <Element> list= new ArrayList <Element>();//接受访问者的访问操作public void accept(Visitor visitor){Iterator i=list. iterator();while(i.hasNext()){//遍历访问集合中的每一个元素((Element)i.next()).accept(visitor);}}public void addElement(Element element){list.add(element) ; }public void remove Element(Element element) { list.remove(element) ;}
}

应用实例

某电力公司云平台中包含一个员工数据系统,员工包括正式员工和非正式员工,每周公司要对员工工作时间、员工工资等数据进行处理。公司有两个部门的主管要对工作人员的情况进行统计,分别是考勤专员和财务专员。考勤专员负责汇总每周员工工作时间,而财务专员负责计算每周员工工资。公司制度如下:

(1)正式员工周薪4000元,每周工作时间为40小时。如果超过40小时,超出部分按照80元/小时作为加班费;如果少于40小时,旷工所扣工资以50元/小时计算,直到基本工资扣除到零为止。

(2)非正式员工时薪80元,基本工资按小时计算。

我们约定:

HRDep表示考勤专员,FADep表示财务专员,这是访问者角色, 其抽象父类Department充当抽象访问者角色。

EmployeeList充当对象结构,用于存储员工列表; FullTimeEmp表示正式员工, PartTimeEmp表示非正式员工, 这是具体元素角色, 其父接口Employee充当抽象元素角色。

(1)Employee:员工类,充当抽象元素类。

package designpatterns.visitor;public interface Employee {public void accept(Department handler); //接受一个抽象访问者访问
}

(2)FullTimeEmp:全职员工类,充当具体元素类。

package designpatterns.visitor;public class FullTimeEmp implements Employee {private String name;     //员工姓名private double weeklyWage;    //员工周薪private int workTime;    //工作时间public FullTimeEmp(String name, double weeklyWage, int workTime){this. name= name;this. weeklyWage= weeklyWage;this. workTime= workTime;}public void setName(String name){this. name= name;}public void setWeeklyWage(double weeklyWage){this. weeklyWage= weeklyWage;}public void setWorkTime(int workTime){this. workTime= workTime;}public String getName(){return(this.name);}public double getWeeklyWage() {return (this. weeklyWage);}public int getWorkTime(){return(this. workTime);}public void accept(Department handler)(handler.visit(this);    //调用访问者的访问方法}
}

(3)PartTimeEmp:兼职员工类,充当具体元素类。

package designpatterns.visitor;public class PartTimeEmp implements Employee {private String name;     //员工姓名private double hourWage;    //员工时薪private int workTime;    //工作时间public PartTimeEmp(String name, double hourWage, int workTime){this. name= name;this. hourWage= hourWage;this. workTime= workTime;}public void setName(String name){this. name= name;}public void setHourWage(double HourWage){this. HourWage= HourWage;}public void setWorkTime(int workTime){this. workTime= workTime;}public String getName(){return(this.name);}public double getHourWage() {return (this. HourWage);}public int getWorkTime(){return(this. workTime);}public void accept(Department handler)(handler.visit(this);    //调用访问者的访问方法}
}

 (4)Department:部门类,充当抽象访问者类。

package designpatterns.visitor;public abstract class Department{//声明一组重载的访问方法,用于访问不同类型的具体元素public abstract void visit(FullTimeEmp employee);public abstract void visit(PartTimeEmp employee);
}

(5)FADep财务专员类,充当具体访问者类。

package designpatterns.visitor;public class FADep extends Department {//实现财务部对全职员工的访问public void visit(FullTimeEmp employee){int workTime= employee.getWorkTime();double weekWage = employee.getWeeklyWage();if(workTime >40){weekWage= weekWage+(workTime-40)*80;}else if(workTime<40){weekWage= weekWage-(40-workTime)*50;if(weekWage<0){weekWage=0;}}System.out.println("正式员工"+employee.getName()+"实际工资为:"+weekWage+"元");}//实现财务部对兼职员工的访问public void visit(PartTimeEmp employee){int workTime= employee.getWorkTime();double hourWage= employee.getHourWage();System.out.println("非正式员工"+employee.getName()+"实际工资为:"+workTime*hourWage+"元");}
}

(6)HRDep:考勤专员类,充当具体访问者类。

package designpatterns.visitor;public class HRDep extends Department{//实现人事部对全职员工的访问public void visit(FullTimeEmp employee){int workTime= employee.getWorkTime();System.out.println("正式员工"+employee.getName()+"实际工作时间为:"+workTime+"小时");if(workTime >40){System.out.println("正式员工"+employee.getName()+"加班时间为:"+(workTime-40)+"小时");}else if(workTime<40){System.out.println("正式员工"+employee.getName()+"请假时间为:"+(40-workTime)+"小时");}}//实现人力资源部对兼职员工的访问public void visit(PartTimeEmp employee){int workTime = employee.getWorkTime();System.out.println("非正式员工"+employee.getName()+"实际工作时间为:"+workTime+"小时");}
}

(7)EmployeeList:员工列表类,充当对象结构。

package designpatterns.visitor;
import java.util.#;public class EmployeeList {//定义一个集合用于存储员工对象private ArrayList <Employee> list= new ArrayList <Employee>();public void addEmployee(Employee employee){list.add(employee);}//遍历访问员工集合中的每一个员工对象public void accept(Department handler){for(Object obj: list){((Employee)obj).accept(handler);}}
}

(8)配置文件config.xml,在配置文件中存储了具体访问者类的类名。

<?xml version= "1.0"?>
<config><className> designpatterns.visitor.FADep </className >
</config>

(9)XMLUtil:工具类。

package designpatterns.visitor;import javax.xml.parsers.*;
import org.w3c.dom.*
import java.io.*;public class XMLUtil {/该方法用于从XML配置文件中提取具体类的类名,并返回一个实例对象public static Object getBean(){try {//创建DOM文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder= dFactory.newDocumentBuilder();Document doc;doc= builder.parse(new File("src//designpatterns//visitor//config,xml"));//获取包含类名的文本结点NodeList nl =doc. getElementsByTagName("className");Node classNode= nl. item(0). getFirstChild();String cName=classNode. getNodeValue();//通过类名生成实例对象并将其返回Class c=Class. forName(cName);Object obj=c. newInstance();return obj;}catch(Exception e){e. printStackTrace();return null;}}
}

(10)Client:客户端测试类。

package designpatterns. visitor;public class Client{public static void main(String args[])(EmployeeList list= new EmployeeList();Employee fte1, fte2, fte3, ptel, pte2;ftel= new FullTimeEmp("员工1",4000.00,50);fte2= new FullTimeEmp("员工2",4000.00,40);fte3= new FullTimeEmp("员工3",4000.00,35);ptel= new PartTimeEmp("员工4",80.00,25);pte2= new PartTimeEmp("员工5",80.00,22);list.addEmployee(ftel);list.addEmployee(fte2);list.addEmployee(fte3);list.addEmployee(pte1);list.addEmployee(pte2);Department dep;dep=(Department)XMLUtil.getBean();list.accept(dep);}
}

输出结果如下:

正式员工员工1实际工资为:4800元。

正式员工员工2实际工资为:4000元。

正式员工员工3实际工资为:3750元。

非正式员工员工4实际工资为:2000元。

非正式员工员工5实际工资为:1760元。

用本人自创的画图法,各类之间的函数执行满足下面的规律:

如果需要更换具体访问者类,无须修改源代码,只需修改配置文件即可。将存储在配置文件config.xml中的具体访问者类FADep改为 HRDep。

重新编译并运行程序,输出结果如下:

正式员工员工1实际工作时间为:50小时。

正式员工员工1加班时间为:10小时。

正式员工员工2实际工作时间为:40小时。

正式员工员工3实际工作时间为:35小时。

正式员工员工3请假时间为:5小时。

非正式员工员工4实际工作时间为:25小时。

非正式员工员工5实际工作时间为:22小时。

总结

这种设计模式的最大好处就是如果要在系统中增加一种新的访问者,无须修改源代码,只要增加一个新的具体访问者类即可,在该具体访问者中封装了新的操作元素对象的方法。从增加新的访问者的角度来看,访问者模式符合开闭原则。所以,访问者模式可以很方便地添加新的访问者。

如果要在系统中增加一种新的具体元素,例如增加一种新的员工类型为“临时人员”,由于原有系统并未提供相应的访问接口,因此必须对原有系统进行修改,在原有的抽象访问者类和具体访问者类中增加相应的访问方法。从增加新的元素的角度来看,访问者模式违背了开闭原则。所以,访问者模式对开闭原则的支持具有倾斜性,添加新的被访问的元素很麻烦。

各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的各类设计模式实现。

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。

Windows环境下实现设计模式——访问者模式(JAVA版)相关推荐

  1. Windows环境下实现设计模式——模板方法模式(JAVA版)

    我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现模板方法模式(设计模式). 不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式 ...

  2. wifidog java_家用环境下部署wifidog认证服务器(java版)

    本文所讲的是基于一个java版wifidog认证服务器的开源项目,在windows环境下搭建wifidog认证服务器配合apfree固件实现用户名密码的认证. 大致步骤如下: 一,准备 1.搭建硬件及 ...

  3. Windows环境部署悟空CRM9.0(JAVA版)开源项目

    小白首次部署项目,过程中收获颇多,以此文记录下来. 一.gitee了解项目基本信息,技术栈 72crm-9.0-Jfinal: 悟空CRM-基于jfinal+vue+ElementUI的前后端分离CR ...

  4. Windows环境下32位汇编程序设计C版code--第四章

    采用的编译环境为VC++6.0 (一)第一个窗口函数 FirstWindow.c#include <windows.h> LRESULT CALLBACK ProcWinMain(HWND ...

  5. Windows环境下32位汇编程序设计C版code--第五章(三)

    (三)窗口子控件 #include <windows.h> #include "resource.h" HINSTANCE hInst; TCHAR szBuffer[ ...

  6. Windows环境下32位汇编程序设计C版code--第五章(二)

    (二)图标和光标 #include <windows.h> #include "Resource.h" TCHAR szName[] = TEXT("Icon ...

  7. Windows环境下32位汇编程序设计C版code--第五章(一)

    (一)菜单和加速键 #include <windows.h> #include "Resource.h" TCHAR szBuffer[128]; TCHAR szCl ...

  8. java 内存映射文件进程间通讯_[转]Windows环境下利用“共享内存”实现进程间通信的C/C++代码---利用CreateFileMapping和MapViewOfFile...

    进程间的通信方式有很多种, 上次我们说了最傻瓜的"共享外存/文件"的方法. 那么, 在本文中, 我们即将学习"共享内存"的方式实现进程间的通信, 这是IPC最快 ...

  9. Windows环境下32位汇编语言程序设计(典藏版)

    <Windows环境下32位汇编语言程序设计(典藏版) > 基本信息 作者: 罗云彬 出版社:电子工业出版社 ISBN:9787121207594 上架时间:2013-7-8 出版日期:2 ...

最新文章

  1. Transformer-LS霸榜ImageNet,输入长度提升三倍!极度压缩参数
  2. /bin/bash: jar: command not found
  3. YoloAll V2发布,集成所有主流Yolo模型于一身
  4. 利用XML实现通用WEB报表打印 卢彦
  5. java 标准输入流 关闭 打开_java--标准输入输出流
  6. 3g造就了电商,4g推动了微商,5g物联网的时代,能够成就什么?
  7. Java多线程(四)——多线程数据隔离与共享
  8. 19. 用 GTK+ 进行GNOME 编程,用 Qt进行KDE 编程
  9. 交通标志 | 中美两国警告标志异同分析
  10. Error: package or namespace load failed for ‘utils’ in dyn.load(file, DLLpath = DLLpath, ...): unab
  11. 2018仲恺农业计算机大类录取,仲恺农业工程学院2019录取分数线预测
  12. 用数组实现一个队列改进版
  13. 利用MATLAB求系统响应
  14. 股票自动委托下单html,股票怎么设置自动挂单?股票交易挂单规则
  15. 2020年煤矿安全监测监控实操考试视频及煤矿安全监测监控作业模拟考试
  16. 2022 年最受欢迎的 19个 VS Code 主题排行榜
  17. IIC通信协议详解 PCF8591应用(Verilog实现FPGA)
  18. APK部署手机上出现闪退现象
  19. 新库上线 | CnOpenDataA股上市公司IPO申报发行文本数据
  20. 奇瑞新能源销量回升,iCar或延续辉煌

热门文章

  1. 树莓派 linux 当服务器,将树莓派打造成音乐播放服务器
  2. libreoffice + jodconverter + Springboot 整合使用将Word转PDF
  3. 解决问题的方法和途径-问题分析
  4. python:将遥感数据使用matplotlib库绘制成图片
  5. 互联网起源 发展 与华尔街(视频)
  6. Hello World的由来
  7. SDTE: A Secure Blockchain-Based Data Trading Ecosystem论文阅读
  8. 疯狂java(三)-数据类型和运算符(疯狂java第4版)
  9. 【SV】 $sformat和$formatf的用法
  10. 人工智能的 A 到 Z | The A to Z of Artificial Intelligence