设计模式之访问者模式
1. 什么是访问者模式
Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新的操作。
换句话说就是访问者模式就是说对于一组对象,在不改变数据结构的前提下,增加作用于这些结构元素新的功能。适用于数据结构相对稳定,它把数据结构和作用于其上的操作解耦,使得操作集合可以相对自由地演化。
访问者模式的结构:
-
访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
-
具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。
-
元素角色(Element):定义一个Accept操作,它以一个访问者为参数。
-
具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。
-
对象结构角色(Object Structure): 这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
优缺点:
优点:
-
符合单一职责原则
-
扩展性良好
-
有益于系统的管理和维护
缺点:
-
增加新的元素类变得很困难
-
破坏封装性,就是访问者需要知道一些类的数据结构,这些类的数据结构就暴露给了访问者。
适用场合:
- 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的
注意事项:
-
系统有比较稳定的数据结构,数据结构不断更新的话访问者也就需要不断的修改。
-
与迭代器的关系,迭代器就是提供访问者的一种注入的方式,访问者需要对对象进行处理,迭代器就是访问,不管他是怎么处理的,但是访问者就是注重对对象的处理的。
2. 具体实例
雇员管理系统,每个员工都有自己的信息,有大家都一样的,也有每个人不一样的。例如每个人的工资,入职时间,工作年限等等。现在雇员管理系统遇到的问题,需要添加一些新的操作功能。那怎么设计呢?
我们想就是两个类,就是员工类和员工管理类,然后实现这些功能。但是当一个人的工资要涨了,休假的时间要变长了,或者要添加新的季度绩效这个评价指标,那就得去修改员工类和管理类。我们这里可以利用访问者模式来设计,就是使用访问者把变化的东西抽象出来,然后从员工类和员工管理类中获取进行处理显示。
设计的结构类图如下:
就是抽象一个vistor的访问者接口,然后由具体的访问者去实现,因为访问者可能有很多个,所以就设计一个接口。
然后在具体的员工中有一个accept方法,将vistor注入进来,然后员工类把自己传给vistor进行处理,这种双向传递是很有意思的。
看一下具体的代码实现:
员工的抽象类:
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 void Accept(Visitor visitor) 把自己传给访问者。
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 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));
}
}
通过一个强制类型转换就把Element基类转换成具体实现的子类。
测试类:
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());
}
}