9. Visitor pattern
visitor pattern (Vistor Pattern) is a design pattern that separates data structure and data manipulation. It refers to encapsulating some operations that act on various elements in a certain data structure, and it can define new operations that act on these elements without changing the data structure.
The basic idea of the visitor pattern is to provide an accept() method to accept the visit of the visitor object for certain fixed types of object structures (elements) in the system. Different visitors have different access contents to the same element, so the same element can produce different element results. That is to say, different visitors access different properties in the same object, resulting in different results.
9.1 Composition structure
- Abstract visitor (Visitor) role: defines the behavior of visiting each element (Element). Its parameters are the elements that can be accessed. The number of its methods is theoretically related to the number of element classes (the number of implementation classes of Element) ) is the same. From this point, it is not difficult to see that the visitor pattern requires that the number of element classes cannot be changed.
- Specific visitor (ConcreteVisitor) role: gives the specific behavior that occurs when visiting each element class.
- Abstract element (Element) role: defines a method (accept) to accept visitors, which means that each element must be accessible by visitors.
- Concrete Element (ConcreteElement) role: Provide a specific implementation of the access method, and this specific implementation usually uses the method provided by the visitor to access the element class.
- Object structure (Object Structure) role: the object structure mentioned in the definition, the object structure is an abstract expression, the specific point can be understood as a class with container properties or composite object characteristics, it will contain a set of elements (Element), And these elements can be iterated for visitors to visit.
9.2 Scene Design
For example, the company’s year-end summary, the company’s CEO and CTO want to see the project status of the year. Here, employees are divided into ordinary employees and project managers. The CEO cares about the employee’s kpi and the number of projects, while the CEO cares about the employee’s kpi and the number of completed projects. The CTO cares about the amount of employee code and the number of completed projects. (This does not mean that different visitors have different access content to the same fixed element, resulting in different results)
9.3 Implementation
abstract visitor
public interface Visitor { //visit staff void visit(Engineer engineer); //access manager void visit(Manager manager); }
specific visitor
//ceo public class CEOVisitor implements Visitor{ @Override public void visit(Engineer engineer) { System.out.println("ceo visiting engineer"); System.out.println("engineer:" + engineer.getName() + "KPI:" + engineer.getKpi()); } @Override public void visit(Manager manager) { System.out.println("ceo interview manager"); System.out.println("manager:" + manager.getName() + "KPI:" + manager.getKpi() + " Projects completed this year:" + manager.getProductNum() + "indivual"); } } //cto public class CTOVisitor implements Visitor{ @Override public void visit(Engineer engineer) { System.out.println("cto visiting engineer"); System.out.println("engineer:" + engineer.getName() + " This year's code volume" + engineer.getCodeLineTotal() + "Row"); } @Override public void visit(Manager manager) { System.out.println("cto access manager"); System.out.println("manager:" + manager.getName() + "KPI:" + manager.getKpi() + " Projects completed this year:" + manager.getProductNum() + "indivual"); } }
abstract interviewee
public interface Employee { //How employees are accessed void accept(Visitor visitor); }
specific interviewee
//Engineer Object Interviewed 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; } } //manager object, visitor 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; } }
structure object
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); }); } }
test class
public class Client { public static void main(String[] args) { EmployeeStructure structure = new EmployeeStructure(); Engineer engineerZ = new Engineer("Xiao Zhang"); Engineer engineerW = new Engineer("Xiao Wang"); Engineer engineerL = new Engineer("Xiao Li"); Manager managerZ = new Manager("boss Zhang"); Manager managerW = new Manager("Mr. Wang"); Manager managerL = new Manager("President Li"); 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 Advantages and disadvantages
advantage:
-
Good scalability
Add new functionality to elements in an object structure without modifying the elements in the object structure.
-
Good reusability
The visitor is used to define functions common to the entire object structure, thereby increasing the degree of reuse.
-
Separation of irrelevant behavior
Use visitors to separate irrelevant behaviors, and encapsulate related behaviors to form a visitor, so that the function of each visitor is relatively single.
shortcoming:
-
Object structure changes are difficult
In the visitor mode, every time a new element class is added, corresponding specific operations must be added to each specific visitor class, which violates the "opening and closing principle".
-
Violates the Dependency Inversion Principle
The visitor pattern relies on concrete classes, not abstract classes.
9.5 Usage Scenarios
-
A program whose object structure is relatively stable, but whose operation algorithm changes frequently.
-
The objects in the object structure need to provide many different and unrelated operations, and the changes of these operations should not affect the structure of the object.