文章目录
一、访问者模式简介
Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作。
二、访问者模式结构
三、访问者模式的角色与职责
- 访问者角色(Visitor): 为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
- 具体访问者角色(Concrete Visitor): 实现每个由访问者角色(Visitor)声明的操作。
- 元素角色(Element): 定义一个Accept操作,它以一个访问者为参数。
- 具体元素角色(Concrete Element): 实现由元素角色提供的Accept操作。
- 对象结构角色(Object Structure): 这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
四、访问者模式的具体实现
年终总结时,公司需要对每个员工的年休假剩余量进行补贴。每个员工都有如下的信息
名字 | name |
---|---|
等级 | level |
剩余天数 | vacationdays |
每日津贴 | allowance |
1、不使用访问者模式
方案设计
类设计
不适用访问者模式的时候,使用最简单的员工类:
// 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类进行修改,违反了开放封闭原则。
这时候我们需要用到访问者模式。
2、使用访问者模式
方案设计
首先我们需要一个访问者和一个被访问者的接口。
被访问者接口中有一个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
可以看到我们没有在改变原先的代码情况下获得了对雇员等级的查询。
五、访问者模式优缺点
优点:
- 符合单一直则原则
- 扩展性良好
- 有益于系统的管理和维护
缺点
- 增加新的元素类变得困难
- 破坏了封装性