9.访问者模式
访问者模式(Vistor Pattern)是一种将数据结构和数据操作分离的设计模式。是指封装一些作用于某种数据结构中的各种元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新操作。
访问者模式的基本思想是针对系统中拥有的某些固定类型的对象结构(元素),在其内部提供一个accept()方法用来接受访问者对象的访问。不同的访问者对同一元素的访问内容不同,所以使得相同的元素可以产生不同的元素结果。就是说不同的访问者访问同一个对象里面的不同属性,产生了不同的结果。
9.1 组成结构
- 抽象访问者(Visitor)角色:定义了对每一个元素
(Element)
访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。 - 具体访问者(ConcreteVisitor)角色:给出对每一个元素类访问时所产生的具体行为。
- 抽象元素(Element)角色:定义了一个接受访问者的方法(
accept
),其意义是指,每一个元素都要可以被访问者访问。 - 具体元素(ConcreteElement)角色: 提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
- 对象结构(Object Structure)角色:定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(
Element
),并且可以迭代这些元素,供访问者访问。
9.2 场景设计
比如公司年终总结,公司CEO、CTO想看下全年项目情况,这里员工分为普通员工和项目经理,CEO在乎的是员工的kpi和项目数,而CEO关心的是员工kpi和完成项目数,而CTO关心的是员工代码量和完成项目数。(这不就不同的访问者对同一个固定元素的访问内容不同,产生不同的结果)
9.3 实现
抽象访问者
public interface Visitor {
//访问员工
void visit(Engineer engineer);
//访问管理者
void visit(Manager manager);
}
具体访问者
//ceo
public class CEOVisitor implements Visitor{
@Override
public void visit(Engineer engineer) {
System.out.println("ceo访问工程师");
System.out.println("工程师:" + engineer.getName() + "KPI:" + engineer.getKpi());
}
@Override
public void visit(Manager manager) {
System.out.println("ceo访管理者");
System.out.println("经理:" + manager.getName() + "KPI:" + manager.getKpi() + " 今年共完成项目:" + manager.getProductNum() + "个");
}
}
//cto
public class CTOVisitor implements Visitor{
@Override
public void visit(Engineer engineer) {
System.out.println("cto访问工程师");
System.out.println("工程师:" + engineer.getName() + " 今年代码量" + engineer.getCodeLineTotal() + "行");
}
@Override
public void visit(Manager manager) {
System.out.println("cto访问管理者");
System.out.println("经理:" + manager.getName() + "KPI:" + manager.getKpi() + " 今年共完成项目:" + manager.getProductNum() + "个");
}
}
抽象被访问者
public interface Employee {
//员工被访问方法
void accept(Visitor visitor);
}
具体被访问者
//工程师对象 被访问者
public class Engineer implements Employee{
private String name;
private int kpi;
Engineer(String name){
this.name = name;
this.kpi = new Random().nextInt(10);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public int getCodeLineTotal(){
return this.kpi * 1000000;
}
}
//管理者对象,被访问者
public class Manager implements Employee{
private String name;
private int kpi;
Manager(String name){
this.name = name;
this.kpi = new Random().nextInt(10);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public int getProductNum(){
return this.kpi * 10;
}
}
结构对象
public class EmployeeStructure {
List<Employee> list = new ArrayList<>();
public EmployeeStructure addEmployee(Employee employee){
list.add(employee);
return this;
}
public void report(Visitor visitor){
list.forEach(employee -> {
employee.accept(visitor);
});
}
}
测试类
public class Client {
public static void main(String[] args) {
EmployeeStructure structure = new EmployeeStructure();
Engineer engineerZ = new Engineer("小张");
Engineer engineerW = new Engineer("小王");
Engineer engineerL = new Engineer("小李");
Manager managerZ = new Manager("张总");
Manager managerW = new Manager("王总");
Manager managerL = new Manager("李总");
structure.addEmployee(engineerZ);
structure.addEmployee(engineerW);
structure.addEmployee(engineerL);
structure.addEmployee(managerZ);
structure.addEmployee(managerW);
structure.addEmployee(managerL);
//structure.report(new CEOVisitor());
structure.report(new CTOVisitor());
}
}
9.4 优缺点
优点:
扩展性好
在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
复用性好
通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
分离无关行为
通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
缺点:
对象结构变化很困难
在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
违反了依赖倒置原则
访问者模式依赖了具体类,而没有依赖抽象类。
9.5 使用场景
-
对象结构相对稳定,但其操作算法经常变化的程序。
-
对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。