Visitor pattern of Java design pattern

Posted by strega on Thu, 18 Nov 2021 02:09:09 +0100

Visitor mode

Introduction

For a completed class hierarchy in the system, we have provided it with an interface to meet the requirements. But what should we do in the face of the new demand? If this is one of the few changes, and you don't have to modify the whole class hierarchy for a demand adjustment, it may be a good idea to modify the original class hierarchy directly.

But what we often encounter is: such demand changes may happen constantly; More importantly, any change in requirements may require you to modify the entire class hierarchy. This kind of similar operations are distributed in different classes, which is not a good phenomenon. We need to refactor this structure.

So, visitor mode may be a good choice for you.

Definition and structure

Visitor mode, as the name suggests, after using this mode, you can improve the functions of existing code by adding additional "visitors" without modifying the existing program structure.

The definition of visitor pattern in the book design pattern is that it represents an operation that acts on each element in an object structure. It allows you to define new operations on elements without changing their classes. It can be seen from the definition that the structure object is a necessary condition for using the visitor pattern, and the structure object must have a method to traverse its own objects. This is similar to the concept of collection in java.

The following is the structure of the visitor pattern:

  1. Visitor role: declare an access operation interface for the specific element role in the object structure. The name and parameters of the operation interface identify the specific element roles that send access requests to specific visitors. This allows visitors to access the element directly through its role specific interface.

  2. Concrete visitor: implement each operation declared by the visitor role.

  3. Element: defines an Accept operation, which takes an accessor as a parameter.

  4. **Concrete element role 😗* Implement the Accept operation provided by the element role.

  5. Object structure role: This is a necessary role to use the visitor pattern. It should have the following characteristics: it can enumerate its elements; A high-level interface can be provided to allow the visitor to access its elements; It can be a composite (composite pattern) or a collection, such as a list or an unordered collection.

A class diagram can more clearly see the structure of the visitor pattern.

So, as the introduction assumes, what should we do to make visitor mode run? First, we will add the accept method to the original class hierarchy. Then put the classes in this class hierarchy into an object structure. Then create the visitor role

give an example

Because no example of using visitor pattern can be found in practical application. I have to borrow the teaching code in thinking in patterns with Java. Slightly modified.

import java.util.*;
import junit.framework.*;

//Concrete Visitor 
interface Visitor {
    
    void visit(Gladiolus g);
    void visit(Runuculus r);
    void visit(Chrysanthemum c);
}

// The Flower hierarchy cannot be changed:
//Element role
interface Flower {
    void accept(Visitor v);
}

//The following three specific element roles
class Gladiolus implements Flower {public void accept(Visitor v) { v.visit(this);}
}

class Runuculus implements Flower {
    public void accept(Visitor v) { v.visit(this);}
}

class Chrysanthemum implements Flower {
    public void accept(Visitor v) { v.visit(this);}
}

// Add the ability to produce a string:
//Implementation specific visitor roles
class StringVal implements Visitor {
    
    String s;
    
    public String toString() { return s; }
    
    public void visit(Gladiolus g) {
        s = "Gladiolus";
    }
    
    public void visit(Runuculus r) {
        s = "Runuculus";
    }
    
    public void visit(Chrysanthemum c) {
        s = "Chrysanthemum";
    }
}

// Add the ability to do "Bee" activities:
//Another specific visitor role
class Bee implements Visitor {
    
    public void visit(Gladiolus g) {
        System.out.println("Bee and Gladiolus");
    }
    
    public void visit(Runuculus r) {
        System.out.println("Bee and Runuculus");
    }
    
    public void visit(Chrysanthemum c) {
        System.out.println("Bee and Chrysanthemum");
    }
}

//This is an object generator
//This is not a complete object structure, but only simulates the elements in the object structure
class FlowerGenerator {
    
    private static Random rand = new Random();
    
    public static Flower newFlower() {
        
        switch(rand.nextInt(3)) {
            default:
            case 0: return new Gladiolus();
            case 1: return new Runuculus();
            case 2: return new Chrysanthemum();
        }
    }
}

//Customer test procedure
public class BeeAndFlowers extends TestCase {
    /*
    Here you can see the process executed by visitor mode:
    First, get a specific visitor role on the client
    Traversal object structure
    Call the accept method on each element to pass in the specific visitor role
    This completes the whole process
    */
    //Object structure roles are assembled here
    List flowers = new ArrayList();
    
    public BeeAndFlowers() {
        for(int i = 0; i < 10; i++)
            flowers.add(FlowerGenerator.newFlower());
    }
    
    Visitor sval ;
    
    public void test() {
    // It's almost as if I had a function to// produce a Flower string representation:
    //This place you can modify to use another specific visitor role
        sval = new StringVal();
        Iterator it = flowers.iterator();
        while(it.hasNext()) {
            ((Flower)it.next()).accept(sval);
            System.out.println(sval);
        }
    }
    
    public static void main(String args[]) {
        junit.textui.TestRunner.run(BeeAndFlowers.class);
    }
}

Double Dispatch

By the way, did you realize the implementation of double dispatch in the above example?

First, pass the concrete visitor pattern as a parameter to the concrete element role in the client program (highlighted). This completes an assignment.

After entering the specific element role, the specific element role calls the visitor method in the specific visitor pattern as a parameter, and passes itself (this) as a parameter. The specific visitor mode can be executed by selecting methods according to different parameters (as shown in the highlighted place). This completes the second assignment.

Advantages, disadvantages and Application

Let's first look at whether the use of visitor mode can avoid the pain in the introduction. After using the visitor mode, for adding new operations to the original class hierarchy, you only need to implement a specific visitor role without modifying the whole class hierarchy. And this meets the requirements of the "opening and closing principle". Moreover, each specific visitor role corresponds to a related operation. Therefore, if the requirements of an operation change, only one specific visitor role can be modified without changing the whole class hierarchy.

It seems that the visitor model can really solve some of the problems we face.

Moreover, because the visitor pattern provides an additional layer of "visitors" for our system, we can add some additional operations on element roles to the visitors.

However, the principle of "opening and closing" is always one-sided. If the class hierarchy in the system changes, what impact will it have on the visitor pattern? You must modify the visitor role and each specific visitor role

It seems that the visitor role is not suitable for situations where the roles of specific elements often change. Moreover, if the visitor role wants to perform operations related to the element role, it must let the element role expose its internal attributes, which means that other objects can also be accessed in java. This destroys the encapsulation of element roles. Moreover, in visitor mode, the information that can be transmitted between elements and visitors is limited, which often limits the use of visitor mode.

The book "design patterns" gives the application of visitor patterns:

  1. An object structure contains many class objects with different interfaces, and you want to perform some operations on these objects that depend on their specific classes.

  2. You need to perform many different and irrelevant operations on objects in an object structure, and you want to avoid making these operations "pollute" the classes of these objects. Visitor allows you to centralize related operations and define them in a class.

  3. When the object structure is shared by many applications, use the Visitor mode to make each application contain only the operations that need to be used.

  4. The class that defines the object structure rarely changes, but it is often necessary to define new operations on this structure. Changing the object structure class requires redefining the interface to all visitors, which can be costly. If object structure classes change frequently, it may be better to define these operations in these classes.

Can you understand it well?

summary

This is a clever and complex model, and its use conditions are relatively harsh. When there is a fixed data structure in the system (such as the class hierarchy above)
With different behaviors, visitor mode may be a good choice.

Topics: Java Back-end