1. Introduction

1.1 define

Visitor Pattern definition: Represents an operation on elements in an object structure that allows us to define new operations on those elements without changing the classes of the elements. The visitor pattern is an object behavior pattern.

1.2 Structure Mode

FIG. 1 Viewer pattern structure diagram

1.3 Mode Analysis

Imagine a usage scenario where there is a list of different types of collections of drugs, quantities, prices, etc. For nurses, to see the information of drugs and quantity is to fetch and carry out relevant medical work, while for paying staff, to see drugs, quantity and price is to check. This is a

  • Background: Multiple types of items inherit a common ancestor and are in a collection.
  • Requirements: There are several types of visitor that make different decisions depending on the actual type of visitor needed.

2. Visitor pattern instance

In the supermarket, customers put selected items such as apples and books into their shopping carts and pay at the cashier. During the shopping process, customers need to access these goods to confirm the quality of these goods, and then the cashier needs to access the goods selected by customers in the shopping cart when calculating the price. At this point, the shopping cart acts as an ObjeetStructure for storing various types of goods, while the customer and cashier act as visitors to these goods, and they need to check and price the goods. Different types of goods may have different forms of access, for example, apples need to be weighed and then priced, while books do not. Use the visitor pattern to design the shopping process.Figure 2 Shopping cart class diagram

public abstract class Visitor
{
	protected String name;

	public void setName(String name){
    	this. name = name;
    }

	public abstract void visit(Apple apple);

	public abstract void visit(Book book);
}
Copy the code
public class Customer extends Visitor {
	public void visit(Apple apple) {
		System. out. println("Customer"+ name + "Apples."); }public void visit(Book book) {
    	System. out. println("Customer"+ name + "Books." ); }}Copy the code
public class Saler extends Visitor {
	public void visit(Apple apple) {
		System. out. println("Cashier"+ name+"Weigh the apples and calculate their price.");    	
    }
    
    public void visit(Apple apple) {
		System. out. print 1n("Cashier"+ name +"Just calculate the price of the book." ); }}Copy the code
public interface Product {
	void accept(Visitor visitor); }Copy the code
public class Apple implements Product {
	public void accept (Visitor visitor) {
    	visitor. visit(this); }}Copy the code
public class Book implements Product {
	public void accept (Visitor visitor) {
    	visitor. visit(this); }}Copy the code
import java. util.*

public class BuyBasket {
	private ArrayList list = new ArrayList();

    public void accept(Visitor visitor) {Iterator I = list.iterator ();while(i.hasNext()) { ((Product)i.next()).accept(visitor); }}public void addProduct (Product product) {1 ist. Add (product); }public void removeProduct (Product product) { list.remove(product); }}Copy the code
public class Client {
	public static void main(String a[]) {
    	Product b1 = new Book();
        Product b2 = new Book();
        
        Producr a1 = new Apple();
        
        BuyBasket basket = newBuyBasket(); Basket. AddProduct (b1); basket.addProduct(b2); basket.addProduct(a1); Visitor customer =newCustomer(); basket.accept(visitor); }}Copy the code

3. Source code analysis: Visitor pattern in Eclipse JDT AST

While iterating through the syntax tree, if we need to add new visitors, we override a new class that inherits the Abstract Class ASTVisitor, and then override the methods we need. All visit methods return true by default; . Now I need to count how many times each class is referenced through the syntax tree, just write a new class that extends the ASTVisitor abstract class, Then override the public Boolean VISIT (MethodDeclaration node) and public Boolean VISIT (FieldDeclaration node) functions, Statistics on the occurrence of referenced classes in child nodes of the syntax tree.

@Override
public class MyVisitor extends ASTVisitor {
    public boolean visit(MethodDeclaration node) {
    	for (Object o : node.parameters()) {// Iterate over function arguments
    	// Get which class the parameter belongs to, and then +1 for the number of occurrences of that class
    }
	
    public boolean visit(FieldDeclaration node) {
    	// Get which class the member variable belongs to, and then +1 the number of occurrences of that class}}Copy the code

How do you call it? Use the compilationUnit. Accept (myVisitor); Can. We found it easy to define new access rules without having to change the CompilationUnit itself, which is the beauty of the visitor pattern. compilationUnit.accept(myVisitor); When the function is executed, the relevant source code for the call is as follows

//ASTNode:
public final void accept(ASTVisitor visitor) {
    if (visitor == null) {
        throw new IllegalArgumentException();
    } else {
        if (visitor.preVisit2(this)) {
            this.accept0(visitor);
        }

        visitor.postVisit(this); }}//ASTVistor:
public boolean preVisit2(ASTNode node) {
        this.preVisit(node);
        return true; }}//ASTVistor:
public void preVisit(ASTNode node) {}//ASTNode:
abstract void accept0(ASTVisitor var1);
//CompilationUnit:
void accept0(ASTVisitor visitor) {
    boolean visitChildren = visitor.visit(this);
    if (visitChildren) {
        this.acceptChild(visitor, this.getPackage());
        this.acceptChildren(visitor, this.imports);
        this.acceptChildren(visitor, this.types);
    }

    visitor.endVisit(this);
}
//ASTNode:
final void acceptChild(ASTVisitor visitor, ASTNode child) {
    if(child ! =null) { child.accept(visitor); }}//ASTNode:
final void acceptChildren(ASTVisitor visitor, ASTNode.NodeList children) {
    ASTNode.NodeList.Cursor cursor = children.newCursor();

    try {
        while(cursor.hasNext()) { ASTNode child = (ASTNode)cursor.next(); child.accept(visitor); }}finally{ children.releaseCursor(cursor); }}//ASTVistor:
public void endVisit(ASTNode node) {}Copy the code

We found that when we called the Accep method, we actually called visitor.previsit (node) first and then visited Package, imports, types (the other classes in this file) in the previous order. The overloading of functions enables different visit operations for different types. The class diagram is shown in Figure 3:

Figure 3 ASTNode, ASTVistor source code

If a new class MyASTNode implements an ASTNode abstract class, add preVisit(MyASTNode node), endVisit(MyASTNode node) to ASTVistor, Visit (MyASTNode node) three functions. However, as the Java language has become more stable, the possibility of adding a new ASTNode is unlikely. Here is a tilt of the open and close principle.

4. To summarize

  1. Advantages of the visitor pattern
    1. Makes it easy to add new access operations. With the visitor pattern, adding a new access operation means adding a new visitor class without modifying the existing library code, complying with the “open closed principle.”
    2. Centralize the access behavior of the element object into a visitor object rather than splitting it into element classes. The responsibility of the class is clearer, which facilitates the reuse of element objects in the object structure. The same object structure can be accessed by multiple different visitors.
  2. Disadvantages of the visitor pattern
    1. In the visitor pattern, adding a new element class means adding a new abstract operation in the abstract visitor role and a corresponding concrete operation in each concrete visitor class, violating the “open closed principle.”
    2. Break encapsulation. The visitor pattern requires the visitor object to access and invoke the operations of each element object, which means that the element object must sometimes expose some of its own internal operations and internal state, otherwise it cannot be accessed by the visitor.
  3. Applicable scenario
    1. An object structure contains objects of many types, and you want to perform operations on these objects that depend on their specific types. An access operation is provided for each specific type of visitor, and objects of different types can have different access operations.
    2. You need to do many different and unrelated operations on objects in an object structure, and you want to avoid having those operations “contaminate” the classes of those objects, and you don’t want to modify those classes when you add new operations. The visitor pattern allows us to centralize related access operations in visitor classes, and object structures can be used by multiple different visitor classes, separating the object itself from the access operations of the object.
    3. The class corresponding to an object in an object structure rarely changes, but it is often necessary to define new operations on this object structure.