原理或定義
访问者模式是对象的行为模式。封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
结构
抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
類圖
案例与代码
本文以雇员管理系统遇到的问题来介绍
雇员管理系统遇到的问题:
需要添加一些新的操作功能,根据剩余年假和工资和级别计算每个员工的补贴。
传统设计:
public class Employee {
private String name;
private float income;
private int vacationDays;
private int degree;
public Employee(String name, float income, int vacationDays, int degree) {
this.name = name;
this.income = income;
this.vacationDays = vacationDays;
this.degree = degree;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setIncome(float income) {
this.income = income;
}
public float getIncome() {
return income;
}
public void setVacationDays(int vacationDays) {
this.vacationDays = vacationDays;
}
public int getVacationDays() {
return vacationDays;
}
public void setDegree(int degree) {
this.degree = degree;
}
public int getDegree() {
return degree;
}
}
public class Employees {
private HashMap<String, Employee> employees;
public Employees() {
employees = new HashMap<String, Employee>();
}
public void Attach(Employee employee) {
employees.put(employee.getName(), employee);
}
public void Detach(Employee employee) {
employees.remove(employee);
}
public Employee getEmployee(String name) {
return employees.get(name);
}
public void getCompensation() {
for (Employee employee : employees.values()) {
System.out
.println(employee.getName()
+ "'s Compensation is "
+ (employee.getDegree()
* employee.getVacationDays() * 100));
}
}
}
public class MainTest {
public static void main(String[] args) {
Employees mEmployees = new Employees();
mEmployees.Attach(new Employee("Tom", 4500, 8, 1));
mEmployees.Attach(new Employee("Jerry", 6500, 10, 2));
mEmployees.Attach(new Employee("Jack", 9600, 12, 3));
mEmployees.getCompensation();
}
}
访问者模式设计方案:
抽象访问者:
public interface Visitor {
abstract public void Visit( Element element );
}
访问者 :
public class CompensationVisitor implements Visitor {
@Override
public void Visit(Element element) {
// TODO Auto-generated method stub
Employee employee = ((Employee) element);
System.out.println(employee.getName() + "'s Compensation is "
+ (employee.getDegree() * employee.getVacationDays() * 10));
}
}
抽象元素类 :
public abstract class Element {
abstract public void Accept(Visitor visitor);
}
元素类 :
public class Employee extends Element {
private String name;
private float income;
private int vacationDays;
private int degree;
public Employee(String name, float income, int vacationDays, int degree) {
this.name = name;
this.income = income;
this.vacationDays = vacationDays;
this.degree = degree;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setIncome(float income) {
this.income = income;
}
public float getIncome() {
return income;
}
public void setVacationDays(int vacationDays) {
this.vacationDays = vacationDays;
}
public int getVacationDays() {
return vacationDays;
}
public void setDegree(int degree) {
this.degree = degree;
}
public int getDegree() {
return degree;
}
@Override
public void Accept(Visitor visitor) {
// TODO Auto-generated method stub
visitor.Visit(this);
}
}
结构对象 :
public class Employees {
private HashMap<String, Employee> employees;
public Employees() {
employees = new HashMap();
}
public void Attach(Employee employee) {
employees.put(employee.getName(), employee);
}
public void Detach(Employee employee) {
employees.remove(employee);
}
public Employee getEmployee(String name) {
return employees.get(name);
}
public void Accept(Visitor visitor) {
for (Employee e : employees.values()) {
e.Accept(visitor);
}
}
}
测试类:
public class MainTest {
public static void main(String[] args) {
Employees mEmployees = new Employees();
mEmployees.Attach(new Employee("Tom", 4500, 8, 1));
mEmployees.Attach(new Employee("Jerry", 6500, 10, 2));
mEmployees.Attach(new Employee("Jack", 9600, 12, 3));
mEmployees.Accept(new CompensationVisitor());
}
}
对于一组对象,在不改变数据结构的前提下,增加作用于这些结构元素新的功能。
适用于数据结构相对稳定,它把数据结构和作用于其上的操作解耦,使得操作集合可以相对自由地演化。
使用場景
1、 假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去
2、假如一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去
優缺點
主要优点有:
1、符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,一方面符合单一职责原则。
2、扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。
缺点主要有:
每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。所以,访问者模式比较适用于对已有功能的重构,比如说,元素类的数据已经基本确定下来不会变了,会变的只是这些元素内的相关操作,这时候,我们可以使用访问者模式对原有的代码进行重构一遍,这样一来,就可以在不修改各个元素类的情况下,对原有功能进行修改。