Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作。
年终总结时,公司需要对每个员工的年休假剩余量进行补贴。每个员工都有如下的信息
名字 | name |
---|---|
等级 | level |
剩余天数 | vacationdays |
每日津贴 | allowance |
不适用访问者模式的时候,使用最简单的员工类:
// An highlighted block
package design.visitor.gys.novisitor;
public class Employee {
private String name;
private int level;
private int vacationdays;
private double allowance;
public Employee(String name, int level, int vacationdays, double allowance) {
super();
this.name = name;
this.level = level;
this.vacationdays = vacationdays;
this.allowance = allowance;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public int getVacationdays() {
return vacationdays;
}
public double getAllowance() {
return allowance;
}
}
接着构建一个ArrayList用于保存全部员工信息。在该类中,直接包含一个返回对所有员工剩余津贴的计算方法。
// An highlighted block
package design.visitor.gys.novisitor;
import java.util.ArrayList;
public class Employees {
private ArrayList<Employee> employees=new ArrayList<>();
public Employees() {
super();
}
public void add(Employee e) {
employees.add(e);
}
public void remove(Employee e) {
employees.remove(e);
}
public Employee getEmployee(int i) {
return employees.get(i);
}
public void getAllowance() {
for(Employee e:employees)
System.out.println(e.getName()+": "+e.getAllowance()*e.getVacationdays());
}
}
人事部门仙子需要进行统计并在年终奖励对这部分补贴进行发放:
// An highlighted block
package design.visitor.gys.novisitor;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employees employees=new Employees();
employees.add(new Employee("张三", 1, 2, 200));
employees.add(new Employee("李四", 3, 4, 400));
employees.add(new Employee("王五", 2, 4, 300));
employees.add(new Employee("赵六", 5, 5, 500));
employees.getAllowance();
}
}
让我们看一下统计的结果:
// An highlighted block
张三: 400.0
李四: 1600.0
王五: 1200.0
赵六: 2500.0
按等级进行的补贴效果很好,每个人的积极性都很高。
现在,人事部门又仅仅需要查看每个人的岗位等级,就需要对Employees类进行修改,违反了开放封闭原则。
这时候我们需要用到访问者模式。
首先我们需要一个访问者和一个被访问者的接口。
被访问者接口中有一个accept()方法来接受访问者,访问者接口中有一个visit()方法来对被访问者进行域访问。
访问者接口:
// An highlighted block
package design.visitor.gys.visitor;
public abstract class Visitor {
public abstract void visit(Element e);
}
被访问者:
// An highlighted block
package design.visitor.gys.visitor;
public abstract class Element {
public abstract void accept(Visitor V);
}
下面定义具体的访问者,重写visit()方法,该访问者仍然实现的时之前的津贴查询工作:
// An highlighted block
package design.visitor.gys.visitor;
public class AllowanceVisitor extends Visitor{
@Override
public void visit(Element e) {
// TODO Auto-generated method stub
Employee ee=(Employee)e;
System.out.println(ee.getName()+": "+ee.getAllowance()*ee.getVacationdays());
}
}
下面对被访者进行定义,只需要在原来的employee类中增加accept方法,让访问者能对被访问者的数据进行操作:
// An highlighted block
package design.visitor.gys.visitor;
public class Employee extends Element{
private String name;
private int level;
private int vacationdays;
private double allowance;
public Employee(String name, int level, int vacationdays, double allowance) {
super();
this.name = name;
this.level = level;
this.vacationdays = vacationdays;
this.allowance = allowance;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public int getVacationdays() {
return vacationdays;
}
public double getAllowance() {
return allowance;
}
@Override
public void accept(Visitor v) {
// TODO Auto-generated method stub
v.visit(this);
}
}
这里实现的是单个的被访问者,接下来是对整个Employees类进行访问:
// An highlighted block
package design.visitor.gys.visitor;
import java.util.ArrayList;
public class Employees {
private ArrayList<Employee> employees=new ArrayList<>();
public Employees() {
super();
}
public void add(Employee e) {
employees.add(e);
}
public void remove(Employee e) {
employees.remove(e);
}
public Employee getEmployee(int i) {
return employees.get(i);
}
public void accept(Visitor v) {
for(Employee e:employees)
e.accept(v);
}
}
现在可以进行访问了:
// An highlighted block
package design.visitor.gys.visitor;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employees employees=new Employees();
employees.add(new Employee("张三", 1, 2, 200));
employees.add(new Employee("李四", 3, 4, 400));
employees.add(new Employee("王五", 2, 4, 300));
employees.add(new Employee("赵六", 5, 5, 500));
AllowanceVisitor av=new AllowanceVisitor();
employees.accept(av);
}
}
让我们看一下运行结果:
// An highlighted block
张三: 400.0
李四: 1600.0
王五: 1200.0
赵六: 2500.0
接下来我们要对查询雇员等级,创建一个Levelvisitor类,并重写visit()方法:
// An highlighted block
package design.visitor.gys.visitor;
public class Levelvisitor extends Visitor{
@Override
public void visit(Element e) {
// TODO Auto-generated method stub
System.out.println(((Employee)e).getName()+" : "+((Employee)e).getLevel());
}
}
在原先的测试代码中我们加入下面两行:
// An highlighted block
Levelvisitor lv=new Levelvisitor();
employees.accept(lv);
测试输出如下:
// An highlighted block
张三: 400.0
李四: 1600.0
王五: 1200.0
赵六: 2500.0
张三 : 1
李四 : 3
王五 : 2
赵六 : 5
可以看到我们没有在改变原先的代码情况下获得了对雇员等级的查询。
优点:
缺点