1 定义
访问者模式(Visitor Pattern)属于行为型设计模式,它主是为了解决在不改变元素对象结构的前提下,接受一个访问者角色,访问者可以进行元素算法的执行,而且算法的执行可以随着访问者的改变而改变,从而使元素中的数据结构和数据算法分离达到松耦合效果。例如在公司部门中,有研发工程师和产品经理两类员工,他们就是上述中的元素,他们接受公司领导的工作报告查阅,领导就是访问者,领导的查阅不会影响到他们原来正常的工作,部门经理或者老板是不同的领导,他们所关心的查阅工作和考核标准是不一样的。
2 实现
访问者模式一般包含5种角色,分别是:
- 抽象元素(Element):用于定义接受访问的接口方法。例如上述定义举例的接受查阅工作的员工。
- 具体元素(ConcreteElement):实现抽象元素的接受方法,方法接收一个访问者对象,并执行访问者对应的访问方法。例如上述定义举例的研发工程师和产品经理,他们都接受领导的工作查阅,并领导对应员工的职位进行不同的工作查阅。
- 抽象访问者(Visitor):用于定义具体元素中要访问的接口方法。例如上述定义举例的领导,领导也有权力范围,他们能查阅研发工程师和产品经理的工作报告,但不能去查看对面楼下小卖部的小姐姐的工作报告。
- 具体访问者(ConcreteVisitor):实现抽象访问者的访问方法,方法接收一个元素对象,并根据实际情况对元素做出相应的算法操作。例如上述定义举例的部门经理和老板,他们对研发工程师和产品经理做出不同的查阅考核。
- 对象结构(ObjectStructure):持有所有元素并提供接受不同访问者来访问所有的元素。例如上述定义举例的工作报告。
抽象元素类,只定义一个接受访问的抽象方法的员工抽象类:
public abstract class Staff {
private String mName;
public Staff(String name) {
mName = name;
}
public String getName() {
return mName;
}
public abstract void accept(Visitor visitor);
}
具体元素,这里是研发工程师和产品经理两类员工:
public class Programmer extends Staff {
public Programmer() {
super("研发工程师");
}
@Override
public void accept(Visitor visitor) {
visitor.visitProgrammer(this);
}
}
public class Product extends Staff {
public Product() {
super("产品经理");
}
@Override
public void accept(Visitor visitor) {
visitor.visitProduct(this);
}
}
抽象访问者,定义了具体的研发工程师和产品经理要访问的接口方法:
public abstract class Visitor {
private String mName;
public Visitor(String name) {
mName = name;
}
public String getName() {
return mName;
}
public abstract void visitProgrammer(Programmer staff);
public abstract void visitProduct(Product staff);
}
具体访问者,这里是老板和部门经理,他们各自实现了对研发工程师和产品经理的访问方法:
public class Boss extends Visitor {
public Boss() {
super("老板");
}
@Override
public void visitProgrammer(Programmer staff) {
System.out.println(getName() + "查看" + staff.getName() + "的工作情况");
}
@Override
public void visitProduct(Product staff) {
System.out.println(getName() + "查看" + staff.getName() + "的工作情况");
}
}
public class Manager extends Visitor {
public Manager() {
super("经理");
}
@Override
public void visitProgrammer(Programmer staff) {
System.out.println(getName() + "查看" + staff.getName() + "的工作情况");
}
@Override
public void visitProduct(Product staff) {
System.out.println(getName() + "查看" + staff.getName() + "的工作情况");
}
}
对象结构,持有所有员工的对象并提供接受不同访问者来访问所有的员工:
public class WorkReport {
private List<Staff> staffList = new LinkedList<>();
public void attach(Staff staff) {
staffList.add(staff);
}
public void accept(Visitor visitor) {
for (Staff staff : staffList) {
// 员工接受领导查阅
staff.accept(visitor);
}
}
}
客户端调用:
public class Main {
public static void main(String[] args) {
WorkReport workReport = new WorkReport();
// 工作报告添加被考察员工对象
workReport.attach(new Programmer());
workReport.attach(new Product());
// 领导查阅工作报告
workReport.accept(new Manager());
workReport.accept(new Boss());
}
}
输出结果:
经理查看研发工程师的工作情况
经理查看产品经理的工作情况
老板查看研发工程师的工作情况
老板查看产品经理的工作情况
4 总结
访问者模式的目的就是当对象有着很多不同且不相关的算法操作,而又不希望这些操作对原来数据结构进行影响时,让对象中的数据结构和数据算法进行分离,使得可以相对自由地组合演化。这种代码结构在遇到常需变化的算法操作的话,通过新增的算法操作的实现是非常容易扩展的,因为增加新的算法操作就只需要增加一个新的访问者;但是如果数据结构是常需变化的话,即经常要增加新的数据结构对象,则显得不太合适,因为能发现的是在抽象访问角色中已经明确了各个元素的访问接口,意味着新增元素的同时还需要修改所有的访问者,这显然是非常糟糕的。所以访问者模式仅适用于数据结构比较稳定又有着常需变化算法的情况。