定义:封装一些作用于某种数据结构(List/Set/Map)中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
一、访问员工信息
1. 访问者接口
public interface IVisitor {
//首先,定义我可以访问普通员工
public void visit(CommonEmployee commonEmployee);
//其次,定义我还可以访问部门经理
public void visit(Manager manager);
}
2. 访问者实现
public class Visitor implements IVisitor{
//访问普通员工,打印出报表
public void visit(CommonEmployee commonEmployee) {
System.out.println(this.getCommonEmployee(commonEmployee));
}
//访问部门经理,打印出报表
public void visit(Manager manager) {
System.out.println(this.getManagerInfo(manager));
}
//组装出基本信息
private String getBasicInfo(Employee employee){
String info = "姓名:"+employee.getName()+"\t";
info = info+"性别:"+(employee.getSex()==Employee.FEMALE?"女":"男")+"\t";
info = info+"薪水:"+employee.getSalary()+"\t";
return info;
}
//组装出部门经理的信息
private String getManagerInfo(Manager manager){
String basicInfo = this.getBasicInfo(manager);
String otherInfo = "业绩:"+manager.getPerformance()+"\t";
return basicInfo + otherInfo;
}
//组装出普通员工信息
private String getCommonEmployee(CommonEmployee commonEmployee){
String basicInfo = this.getBasicInfo(commonEmployee);
String otherInfo = "工作:"+commonEmployee.getJob()+"\t";
return basicInfo + otherInfo;
}
}
3. 抽象员工类
public abstract class Employee {
public final static int MALE = 0;//0代表是男性
public final static int FEMALE = 1;//1代表是女性
//甭管是谁,都有工资
private String name;
//只要是员工那就有薪水
private int salary;
//性别很重要
private int sex;
get()/set();
//我允许一个访问者访问
public abstract void accept(IVisitor visitor);
}
4. 普通员工
public class CommonEmployee extends Employee{
//工作内容,这非常重要,以后的职业规划就是靠它了
private String job;
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
//我允许访问者访问
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
5. 管理层员工
public class Manager extends Employee{
//这类人物的职责非常明确:业绩
private String performance;
public String getPerformance() {
return performance;
}
public void setPerformance(String performance) {
this.performance = performance;
}
//部门经理允许访问者访问
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
6. 场景类
public class Client {
public static void main(String[] args) {
for(Employee emp:mockEmployee()){
emp.accept(new Visitor());
}
}
//模拟出公司的人员情况,我们可以想象这个数据是通过持久层传递过来的
public static List<Employee> mockEmployee(){
List<Employee> empList = new ArrayList<Employee>();
//产生张三这个员工
CommonEmployee zhangSan = new CommonEmployee();
zhangSan.setJob("编写Java程序,绝对的蓝领、苦工加搬运工");
zhangSan.setName("张三");
zhangSan.setSalary(1800);
zhangSan.setSex(Employee.MALE);
empList.add(zhangSan);
//产生李四这个员工
CommonEmployee liSi = new CommonEmployee();
liSi.setJob("页面美工、审美素质太不流行了");
liSi.setName("李四");
liSi.setSalary(1900);
liSi.setSex(Employee.FEMALE);
empList.add(liSi);
//再产生一个经理
Manager wangWu = new Manager();
wangWu.setName("王五");
wangWu.setPerformance("基本上是负值,但是我会拍马屁呀");
wangWu.setSalary(18750);
wangWu.setSex(Employee.MALE);
empList.add(wangWu);
return empList;
}
}
二、访问者模式的定义

1. 抽象元素
public abstract class Element {
//定义业务逻辑
public abstract void doSomething();
//允许谁来访问
public abstract void accept(IVisitor visitor);
}
2. 具体元素
public class ConcreteElement1 extends Element{
//完善业务逻辑
public void doSomething() {
//业务处理
}
//允许那个访问者访问
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
&
public class ConcreteElement2 extends Element{
//完善业务逻辑
public void doSomething() {
//业务处理
}
//允许那个访问者访问
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
3. 抽象访问者
public interface IVisitor {
//可以访问哪些对象
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}
4. 具体访问者
public class Visitor implements IVisitor{
//访问el1元素
public void visit(ConcreteElement1 el1) {
el1.doSomething();
}
//访问el2元素
public void visit(ConcreteElement2 el2) {
el2.doSomething();
}
}
5. 结构对象
public class ObjectStruture {
//对象生成器,这里通过一个工厂方法模式模拟
public static Element createElement(){
Random rand = new Random();
if(rand.nextInt(100)>50){
return new ConcreteElement1();
}else{
return new ConcreteElement2();
}
}
}
6. 场景类
public class Client {
public static void main(String[] args) {
for(int i = 0;i < 10;i++){
//获得元素对象
Element el = ObjectStruture.createElement();
//接受访问者访问
el.accept(new Visitor());
}
}
}
三、访问者模式的应用
1. 优点
符合单一职责原则:具体元素角色也就是Employee抽象类的两个子类负责数据的加载,而Visitor类则负责报表的展现,两个不同的职责非常明确地分离开来。
优秀的扩展性:由于职责分开,继续增加对数据的操作非常快捷,如,现在要增加一份给大老板的报表,这份报表格式又有所不同,直接在Visitor中增加一个方法,传递数据后进行整理打印。
2. 缺点
具体元素对访问者公布细节:访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的。
具体元素变更比较困难:具体元素角色的增加、删除、修改都是比较困难的,就上面那个例子,要是想增加一个成员变量,如年龄age,Visitor就需要修改,如果Visitor是一个还好办,多个就很复杂了。
3. 使用场景
1)一个对象结构(List/Set/Map等)包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就是说迭代器模式已经不能胜任的情景。
2)需要对一个对象结构(List/Set/Map等)中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。