1.什么是访问者模式?
表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变个元素的前提下定义作用于这些元素的新操作。
举个栗子:同一个处方单,划价人员计算价格。药房工作人员拿药品。
2.访问者模式结构
(1)Visitor(抽象访问者):抽象访问者为对象结构中的每一个具体元素类声明一个访问操作,从这个操作的名称或参数可以清楚地额知道需要访问的具体元素的类型。
(2)ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结果中一种类型的元素。
(3)Element(抽象元素):一般是抽象类或接口,声明一个accept()方法,用于接收访问者的访问操作,该方法通常以一个抽象访问者作为参数
(4)ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作
(5)ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。对象结构可以结合组合模式实现,也可以是一个简单的集合对象。
3.访问者模式实现
(1)抽象访问者
/**
* 抽象访问者
*/
public abstract class Visitor {
public abstract void visit(ConcreteElementA elementA);
public abstract void visit(ConcreteElementB elementB);
public void visit(ConcreteElementC elementC){
//元素ConcreteElementC操作代码
}
}
(2)具体访问者
/**
* 具体访问者
*/
public class ConcreteVisitor extends Visitor {
@Override
public void visit(ConcreteElementA elementA) {
//元素ConcreteElementA操作代码
}
@Override
public void visit(ConcreteElementB elementB) {
//元素ConcreteElementB操作代码
}
}
(3)元素类
/**
* 元素类
*/
public interface Element {
public void accept(Visitor visitor);
}
(4)具体元素类
/**
* 具体元素类
*/
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void operationA(){
//业务方法
}
}
(5)对象结构
/**
* 对象结构
*/
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 removeElement(Element element){
list.remove(element);
}
}
4.访问者模式实例——正式工工时计算和临时工工时计算
(1)员工类,充当抽象元素类
/**
* 员工类,充当抽象元素类
*/
public interface Employee {
public void accept(Department handler);//接受一个抽象访问者访问
}
(2)全职员工类,充当具体元素类
/**
* 全职员工类,充当具体元素类
*/
public class FulltimeEmployee implements Employee {
private String name;//员工姓名
private double weeklyWage;//员工周薪
private int workTime;//工作时间
public FulltimeEmployee(String name,double weeklyWage,int workTime){
this.name = name;
this.weeklyWage = weeklyWage;
this.workTime = workTime;
}
@Override
public void accept(Department handler) {
handler.visit(this);//调用访问者的访问方法
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeeklyWage() {
return weeklyWage;
}
public void setWeeklyWage(double weeklyWage) {
this.weeklyWage = weeklyWage;
}
public int getWorkTime() {
return workTime;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
}
(3)兼职员工类,充当具体元素类
/**
* 兼职员工类,充当具体元素类
*/
public class ParttimeEmployee implements Employee {
private String name;//员工姓名
private double hourWage;//员工周薪
private int workTime;//工作时间
public ParttimeEmployee(String name,double hourWage,int workTime){
this.name = name;
this.hourWage = hourWage;
this.workTime = workTime;
}
@Override
public void accept(Department handler) {
handler.visit(this);//调用访问者的访问方法
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHourWage() {
return hourWage;
}
public void setHourWage(double hourWage) {
this.hourWage = hourWage;
}
public int getWorkTime() {
return workTime;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
}
(4)部门类,充当抽象访问者类
/**
* 部门类,充当抽象访问者类
*/
public abstract class Department {
//声明一组重载的访问方法,用于访问不同类型的具体元素
public abstract void visit(FulltimeEmployee employee);
public abstract void visit(ParttimeEmployee employee);
}
(5)财务部类,充当具体访问者类
/**
* 财务部类,充当具体访问者类
*/
public class FADepartment extends Department {
//财务部对全职员工的访问
@Override
public void visit(FulltimeEmployee employee) {
int workTime = employee.getWorkTime();
double weekWage = employee.getWeeklyWage();
if(workTime >= 40){
weekWage = weekWage + (workTime - 40)*100;
}else {
weekWage = weekWage - (40 - workTime)*80;
if(weekWage < 0){
weekWage = 0;
}
}
System.out.println("正式员工"+employee.getName()+"实际工资为:"+weekWage+"元");
}
//实现财务部对兼职员工的访问
@Override
public void visit(ParttimeEmployee employee) {
int workTime = employee.getWorkTime();
double hourWage = employee.getHourWage();
System.out.println("临时工"+employee.getName()+"实际工资为:"+workTime*hourWage+"元");
}
}
(6)人力资源部类,充当具体访问者类
/**
* 人力资源部类,充当具体访问者类
*/
public class HRDepartment extends Department {
//实现人力资源部对正式员工的访问
@Override
public void visit(FulltimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("正式员工"+employee.getName()+"实际工作时间为:"+workTime+"小时");
if(workTime>40){
System.out.println("正式员工"+employee.getName()+"加班时间为:"+(workTime-40)+"小时");
}else {
System.out.println("正式员工"+employee.getName()+"请假时间为:"+(40-workTime)+"小时");
}
}
//实现人力资源部对兼职员工的访问
@Override
public void visit(ParttimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("临时工"+employee.getName()+"实际工作时间为:"+workTime+"小时");
}
}
(7)员工列表类,充当对象结构
/**
* 员工列表类,充当对象结构
*/
public class EmployeeList {
//定义一个集合用于存储员工对象
private ArrayList<Employee> list = new ArrayList<Employee>();
public void addElement(Employee employee){
list.add(employee);
}
//遍历员工集合中的每一个员工对象
public void accept(Department handler){
for(Object obj : list){
((Employee)obj).accept(handler);
}
}
}
(8)配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<className>controller.visitorModuleOa.FADepartment</className>
</config>
(9)工具类
public class XMLUtil {
/**
* 从xml配置文件中提取具体类的类名,并返回一个实例对象
*/
public static Object getBean(){
try {
//创建DOM文档对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
Document document = builder.parse(new File("src//controller//visitorModuleOa//config.xml"));
//获取包含类名的文本节点
NodeList nl = document.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)客户端类
public class Client {
public static void main(String[] args) {
EmployeeList list = new EmployeeList();
Employee fte1,fte2,fte3,pte1,pte2;
fte1 = new FulltimeEmployee("张无忌",3200.00,45);
fte2 = new FulltimeEmployee("杨过",2000.00,40);
fte3 = new FulltimeEmployee("段誉",2400,38);
pte1 = new ParttimeEmployee("洪七公",80.00,20);
pte2 = new ParttimeEmployee("郭靖",60.00,18);
list.addElement(fte1);
list.addElement(fte2);
list.addElement(fte3);
list.addElement(pte1);
list.addElement(pte2);
Department department;
department = (Department) XMLUtil.getBean();
list.accept(department);
}
}
(11)路径及结果
5.访问者模式可与组合模式联用
6.访问者模式优缺点
优:
(1)增加新的访问操作很方便
(2)将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中
(3)让用户不修改现有元素类层次结构的情况下定义作用该层次结构的操作
缺:
(1)增加新的元素类很困难
(2)破坏了对象的封装性
7.访问者模式适用环境
(1)一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作
(2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作污染这些对象的类,也不希望在新增时修改这些类
(3)对象结构中对象对应的类很少改变,但是经常需要在此对象结构上定义新的操作。