1.概念
访问者模式是一种复杂的行为型设计模式,包含了访问者和被访问者,通常的情况是访问者需要对不同的被访问者做不同的操作,举个例子来说,医院的药单,通常访问者比如医生和会计都会访问药单,但是这两者对药单的操作是不同的,医生侧重于抓药,而会计则会根据药来计算费用;在软件开发过程中,有时候我们也需要去处理这种集合类的元素,对集合中不同的对象又有不同的访问者需要进行不同的访问处理方式,这种模式可以考虑使用访问者模式;
2.组成
引用一个图来说明下访问者模式的组成部分:
1>Visitor:抽象的访问者,为集合中每一个具体的对象元素定义了一个访问操作;这里对操作的定义有两种方式,一种是
不同的函数命名,针对不同的对象定义不同的名称,比如访问A对象的操作方法visitConcreteElementA,访问B对象的方法visitConcreteElementB....,另外一种方法是定义相同的函数名称,参数不同,就是重载,比如访问A对象的方法visit(ElementA a),访问B对象的方法visit(ElementB b);
2>ConcreteVisitor:具体访问者,对集合中的对象的具体访问的细节;
3>Element:抽象的元素对象,一般是类或者接口,集合中的所有的对象都会继承它,一般有一个accept方法来接受访问者的访问;
4>ConcreteElement:具体的元素,继承元素接口,实现了accept方法,在accept方法中调用访问者的访问方法以便完成访问过程;
5>ObjectStructure:对象结构,一般是一个集合,其中包含了很多Element,并且提供了遍历内部元素的方法,一般是通过迭代器循环调用元素的accept方法完成的;
3.一个例子
比如现在有一个需求:公司要给银行开发一套Oa系统,该银行员工包括正式员工和临时工,每周人力资源部和财务部等部门需要对员工数据进行汇总,汇总数据包括员工工作时间、员工工资等。该公司基本制度如下:
(1) 正式员工(Full time Employee)每周工作时间为40小时,不同级别、不同部门的员工每周基本工资不同;如果超过40小时,超出部分按照100元/小时作为加班费;如果少于40小时,所缺时间按照请假处理,请假所扣工资以80元/小时计算,直到基本工资扣除到零为止。除了记录实际工作时间外,人力资源部需记录加班时长或请假时长,作为员工平时表现的一项依据。
(2) 临时工(Part time Employee)每周工作时间不固定,基本工资按小时计算,不同岗位的临时工小时工资不同。人力资源部只需记录实际工作时间。
人力资源部和财务部工作人员可以根据各自的需要对员工数据进行汇总处理,人力资源部负责汇总每周员工工作时间,而财务部负责计算每周员工工资。
常规的思路是这样的,代码如下所示:
public class EmployList {
private ArrayList<Employee> list = new ArrayList<Employee>(); //员工集合
//增加员工
public void addEmployee(Employee employee) {
list.add(employee);
}
//处理员工数据
public void handle(String departmentName) {
if (departmentName.equalsIgnoreCase("财务部")) //财务部处理员工数据
{
for (Object obj : list) {
if (obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee")) {
System.out.println("财务部处理全职员工数据!");
} else {
System.out.println("财务部处理兼职员工数据!");
}
}
} else if (departmentName.equalsIgnoreCase("人力资源部")) //人力资源部处理员工数据
{
for (Object obj : list) {
if (obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee")) {
System.out.println("人力资源部处理全职员工数据!");
} else {
System.out.println("人力资源部处理兼职员工数据!");
}
}
}
}
}
这样做的弊端:
1>EmployeeList类太庞大了,要把所有的处理逻辑都放在里面,违背“单一职责原则”;
2>代码大量的if else,扩展性太差了,如果现在要增加新的员工类型,势必要做大量的修改;要增加部分也是如此,扩展性太差了;
考虑使用访问者模式:
类关系图如下所示
实现的代码
public interface Employee {
void accept(Department handler);
}
public class FulltimeEmployee implements Employee {
private String name;
private double hourWage; //每周基本工资
private int workTime;
public FulltimeEmployee(String name, double hourWage, int workTime) {
this.name = name;
this.hourWage = hourWage;
this.workTime = workTime;
}
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;
}
@Override
public void accept(Department handler) {
handler.visit(this);
}
@Override
public String toString() {
return "FulltimeEmployee{" +
"name='" + name + '\'' +
", hourWage=" + hourWage +
", workTime=" + workTime +
'}';
}
}
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;
}
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;
}
@Override
public void accept(Department handler) {
handler.visit(this);
}
}
public abstract class Department {
public abstract void visit(FulltimeEmployee employee);
public abstract void visit(ParttimeEmployee employee);
}
public class FADepartment extends Department {
private double totolPay;
public double getTotolPay() {
return totolPay;
}
public void setTotolPay(double totolPay) {
this.totolPay = totolPay;
}
public void visit(FulltimeEmployee employee) {
int workTime = employee.getWorkTime();
double weekWage = employee.getWorkTime();
if (workTime > 40) {
weekWage = weekWage + (workTime - 40) * 100;
} else if (workTime < 40) {
weekWage = weekWage - (40 - workTime) * 80;
if (weekWage < 0) {
weekWage = 0;
}
}
totolPay += weekWage;
System.out.println(employee.getName() + "工资:" + weekWage);
}
public void visit(ParttimeEmployee employee) {
int workTime = employee.getWorkTime();
double hourWage = employee.getHourWage();
System.out.println("临时工" + employee.getName() + "实际工资为:" + workTime * hourWage + "元。");
}
}
public class HrDepartment extends Department {
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 if (workTime < 40) {
System.out.println("正式员工" + employee.getName() + "请假时间为:" + (40 - workTime) + "小时。");
}
}
public void visit(ParttimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("临时工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
}
}
public class Company {
private List<Employee> employeeList = new ArrayList<>();
public void addEmployee(Employee employee) {
this.employeeList.add(employee);
}
public void accept(Department department) {
this.employeeList.forEach(e -> e.accept(department));
}
}
测试代码
public static void main(String[] args) {
Company company = new Company();
Employee fte1,fte2,fte3,pte1,pte2;
fte1 = new FulltimeEmployee("张无忌",3200.00,45);
fte2 = new FulltimeEmployee("杨过",2000.00,40);
fte3 = new FulltimeEmployee("段誉",2400.00,38);
pte1 = new ParttimeEmployee("洪七公",80.00,20);
pte2 = new ParttimeEmployee("郭靖",60.00,18);
company.addEmployee(fte1);
company.addEmployee(fte2);
company.addEmployee(fte3);
company.addEmployee(pte1);
company.addEmployee(pte2);
Department department = new FADepartment();
company.accept(department);
System.out.println(((FADepartment) department).getTotolPay());
}