一、描述
访问者模式是一种将数据结构与数据操作分离开来的行为模式。每个访问者标识一个作用于某对象结构中各元素的操作,它可以使你在不改变各元素的类的前提下定义作用于这些元素的新操作。
角色:
1.结构类:包含元素类,添加访问者类。
2.抽象元素类:定义接收访问者访问的抽象方法。
3.具体元素类:调用访问者访问当前元素的方法
4.抽象访问者类:为对象结构中的具体元素类定义一种访问方法。
5.具体访问者类:实现抽象访问者类中的访问方法
类图:
二、优点
1.符合单一职责原则。各个访问者只需专注于自己的领域即可。
2.扩展性好。可以扩展多个访问者以及元素。
3.低耦合。结构和操作分离开,耦合度低。
三、缺点
1.代码冗余,结构复杂。
2.元素的结构一旦改变,则对应的具体元素类和访问类都要随之改变。
四、适用场景
适合于结构稳定的数据,允许数据结构与操作分离开来的情形。
五、示例
以“行政人员和财务人员查看部门员工信息”为例。其中,部门为结构类,员工为元素,行政和财务人员为访问类。代码如下:
1.部门类,包含员工以及访问者
public class DeptStructure {
//员工列表
private List<AbstractEmployee> employees = new ArrayList<>();
/**
* 添加员工
*
* @param employee
*/
public void addEmployee(AbstractEmployee employee) {
employees.add(employee);
}
/**
* 添加访问者
*
* @param iVisitor
*/
public void attach(IVisitor iVisitor) {
for (AbstractEmployee employee : employees) {
employee.accept(iVisitor);
}
}
}
2.抽象员工类,定义其属性以及接收访问者的抽象方法
@Data
public abstract class AbstractEmployee {
/** 编号 */
private Integer serialNumber;
/** 名字 */
private String name;
/** 年龄 */
private Integer age;
/** 性别 */
private String sex;
/** 薪酬 */
private BigDecimal salary;
/**
* 接收访问者的访问
*
* @param visitor
*/
public abstract void accept(IVisitor visitor);
}
3.具体员工类-组长
public class GroupLeader extends AbstractEmployee {
/**
* 接收访问者的访问
*
* @param visitor
*/
@Override
public void accept(IVisitor visitor) {
visitor.visitGroupLeader(this);
}
}
4.具体员工类-项目经理
public class Manager extends AbstractEmployee {
/**
* 接收访问者的访问
*
* @param visitor
*/
@Override
public void accept(IVisitor visitor) {
visitor.visitManager(this);
}
}
5.抽象访问者,定义访问元素的抽象方法
public abstract class IVisitor {
/**
* 访问组长
*
* @param groupLeader
*/
abstract void visitGroupLeader(GroupLeader groupLeader);
/**
* 访问项目经理
*/
abstract void visitManager(Manager manager);
}
6.具体访问者-行政人员,主要关注当前人的基本信息,除了薪酬
public class AdministrativeVisit extends IVisitor {
@Override
void visitGroupLeader(GroupLeader groupLeader) {
System.out.println("小组,工号:" + groupLeader.getSerialNumber()
+ ",姓名:" + groupLeader.getName()
+ ",年龄:" + groupLeader.getAge()
+ ",性别:" + groupLeader.getSex());
}
@Override
void visitManager(Manager manager) {
System.out.println("项目经理,工号:" + manager.getSerialNumber()
+ ",姓名:" + manager.getName()
+ ",年龄:" + manager.getAge()
+ ",性别:" + manager.getSex());
}
}
7.具体访问者-财务人员,主要关注当前人的工号以及薪酬
public class FinancialPersonnelVisit extends IVisitor {
@Override
void visitGroupLeader(GroupLeader groupLeader) {
System.out.println("组长,工号:" + groupLeader.getSerialNumber() + ",薪酬:" + groupLeader.getSalary());
}
@Override
void visitManager(Manager manager) {
System.out.println("项目经理,工号:" + manager.getSerialNumber() + ",薪酬:" + manager.getSalary());
}
}
8.调用者
public class Client {
public static void main(String[] args) {
DeptStructure deptStructure = new DeptStructure();
GroupLeader groupLeader = new GroupLeader();
groupLeader.setSerialNumber(1);
groupLeader.setName("小美");
groupLeader.setAge(18);
groupLeader.setSalary(BigDecimal.valueOf(5000));
groupLeader.setSex("女");
deptStructure.addEmployee(groupLeader);
Manager manager = new Manager();
manager.setSerialNumber(2);
manager.setName("李四");
manager.setAge(19);
manager.setSalary(BigDecimal.valueOf(7000));
manager.setSex("男");
deptStructure.addEmployee(manager);
//行政
System.out.println("=================行政人员查看员工信息=================");
deptStructure.attach(new AdministrativeVisit());
//财务
System.out.println("=================财务人员查看员工信息=================");
deptStructure.attach(new FinancialPersonnelVisit());
}
}
实现效果
六、疑问
之前在研究该模式的时候,有个疑问,这个模式是否允许对结构中元素进行修改?
答案是不可以的。因为一个元素可能存在多个访问者访问,如果其中一个访问者对其进行修改,那么后续对该元素访问的访问者获取的信息就不是当前元素的原有信息了,而是获取的错误信息。