sheng's learning notes - Design Pattern - Visitor pattern and dual distribution

Keywords: Design Pattern

schematic diagram:

 

In real life, some collection objects have many different elements, and each element also has many different visitors and processing methods. For example, there are multiple scenic spots and multiple tourists in the park. Different tourists may have different comments on the same scenic spot; The prescription list issued by the hospital doctor contains a variety of drug elements. The price assessors and pharmacy staff treat it differently. The price assessors price the drugs according to the name and quantity on the prescription list, and the pharmacy staff fill the drugs according to the contents of the prescription list.

There are many such examples. For example, the characters in movies or TV dramas are evaluated differently by different audiences; There are also goods that customers put in the "shopping cart" when shopping in the mall. Customers are mainly concerned about the cost performance of the selected goods, while cashiers are concerned about the price and quantity of goods.

These processed data elements are relatively stable and accessed in a variety of ways data structure , it is more convenient to use "visitor mode". Visitor mode can separate the processing method from the data structure, and can add new processing methods as needed without modifying the original program code and data structure, which improves the scalability and flexibility of the program.

Definition and characteristics of pattern

 

Definition of Visitor pattern: separate the operations acting on each element in a data structure and package them into independent classes, so that they can add new operations acting on these elements without changing the data structure, so as to provide a variety of access methods for each element in the data structure. It separates the data operation from the data structure. It is the most complex pattern in the behavior pattern.

Visitor pattern is an object behavior pattern. Its main advantages are as follows.

  1. Good scalability. It can add new functions to the elements in the object structure without modifying the elements in the object structure.
  2. Good reusability. Visitors can define the general functions of the whole object structure, so as to improve the reuse degree of the system.
  3. Good flexibility. Visitor mode decouples the data structure from the operations acting on the structure, so that the operation set can evolve relatively freely without affecting the data structure of the system.
  4. Comply with the principle of single responsibility. Visitor mode encapsulates the relevant behaviors to form a visitor, so that the function of each visitor is relatively single.


The main disadvantages of the Visitor pattern are as follows.

  1. Adding new element classes is difficult. In the visitor mode, every time a new element class is added, the corresponding specific operation must be added to each specific visitor class, which violates the "opening and closing principle".
  2. Destroy the package. In the visitor pattern, specific elements publish details to visitors, which destroys the encapsulation of objects.
  3. Violation of the dependency inversion principle. The visitor pattern relies on concrete classes instead of abstract classes.

Structure and implementation of pattern

The key to the implementation of Visitor pattern is how to separate the operations acting on elements and package them into independent classes. Its basic structure and implementation method are as follows.

1. Structure of the model

The visitor pattern contains the following main roles.

  1. Abstract Visitor role: define an interface for accessing concrete elements. Each concrete element class corresponds to an access operation visit(). The parameter type in the operation identifies the accessed concrete elements.
  2. Concrete visitor role: it implements each access operation declared in the abstract visitor role and determines what visitors should do when accessing an element.
  3. Abstract Element role: declare an interface containing the accept operation accept(), and the accepted visitor object is used as the parameter of the accept() method.
  4. Concrete element role: implement the accept() operation provided by the abstract element role. Its method body is usually visitor.visit(this). In addition, the specific element may also contain relevant operations of its own business logic.
  5. Object Structure role: it is a container containing element roles and provides methods for visitor objects to traverse all elements in the container. It is usually implemented by aggregate classes such as List, Set and Map.

code:

package design.visitor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * In real life, some collection objects have many different elements, and each element also has many different visitors and processing methods.
 * For example, there are multiple scenic spots and multiple tourists in the park. Different tourists may have different comments on the same scenic spot
 * For example, the characters in movies or TV dramas are evaluated differently by different audiences
 * These processed data elements are relatively stable and have a variety of data structures accessed. It is more convenient to use "visitor mode" to process them.
 * Visitor mode can separate processing methods from data structures and add new processing methods as needed,
 * The original program code and data structure need not be modified, which improves the expansibility and flexibility of the program
 * <p>
 * Definition of Visitor pattern: separate the operations acting on each element in a data structure and encapsulate them into independent classes,
 * So that it can add new operations on these elements without changing the data structure, and provide a variety of access methods for each element in the data structure.
 * It separates the data operation from the data structure. It is the most complex pattern in the behavior pattern.
 */
public class VisitorPattern {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor = new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("------------------------");
        visitor = new ConcreteVisitorB();
        os.accept(visitor);
    }
}

//Abstract Visitor 
interface Visitor {
    void visit(ConcreteElementA element);

    void visit(ConcreteElementB element);
}

//Specific visitor class A
class ConcreteVisitorA implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("Specific visitors A visit-->" + element.operationA());
    }

    public void visit(ConcreteElementB element) {
        System.out.println("Specific visitors A visit-->" + element.operationB());
    }
}

//Specific visitor class B
class ConcreteVisitorB implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("Specific visitors B visit-->" + element.operationA());
    }

    public void visit(ConcreteElementB element) {
        System.out.println("Specific visitors B visit-->" + element.operationB());
    }
}

//Abstract element class
interface Element {
    void accept(Visitor visitor);
}

//Specific element class A
class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationA() {
        return "Specific elements A Operation of.";
    }
}

//Specific element class B
class ConcreteElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationB() {
        return "Specific elements B Operation of.";
    }
}

//Object structure role
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();

    public void accept(Visitor visitor) {
        Iterator<Element> i = list.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(visitor);
        }
    }

    public void add(Element element) {
        list.add(element);
    }

    public void remove(Element element) {
        list.remove(element);
    }
}

result:

Specific visitor A accesses -- > the operation of specific element A.
The operation of specific visitor A accessing -- > specific element B.
------------------------
Specific visitor B accesses -- > the operation of specific element A.
Specific visitor B accesses -- > the operation of specific element B.

Application examples of pattern

[example 1] use the "Visitor mode" to simulate the functions of art companies and mint companies.

Analysis: art companies can design bronze statues with "copper" and draw pictures with "paper"; Coinage companies can print copper coins with "copper" and paper coins with "paper"( Click here to download the picture to be displayed after running the program ). For the two elements of "copper" and "paper", the treatment methods of the two companies are different, so the visitor mode is more suitable for this example.

First, define a Company interface, which is an abstract visitor and provides two methods to create works based on Paper or copper elements; Redefine the art Company class and Mint class, which are concrete visitors and implement the methods of the parent interface.

Then, define a Material interface, which is an abstract element and provides the accept (Company visitor) method to accept the access of the Company object; Then define the Paper class and copper class, which are specific element classes and implement the methods in the parent interface.

Finally, define a material set (SetMaterial) class, which is an object structure role, has a container List that holds all elements, and provides an accept (Company visitor) method that allows the visitor object to traverse all elements in the container; The client class is designed as a form program. It provides a material set object for the visitor (Company) object to access, and implements the ItemListener interface to process the user's event request. Figure 2 shows its structure.

Mode extension

Visitor mode is a frequently used mode Design pattern , it is often used in conjunction with the following two design patterns.

(1) And“ Iterator mode ”Combined use. Because the "object structure" in the visitor pattern is a container containing element roles, iterators are often used when visitors traverse all the elements in the container. For example, the object structure in [example 1] is implemented by List, which obtains the iterator through the Iterator() method of the List object. If the aggregate class in the object structure does not provide an iterator, you can also customize one with the iterator mode.

(2) The Visitor mode is the same as“ Combination mode ”Combined use. Because the "element object" in the Visitor pattern may be a leaf object or a container object, it must be used if the element object contains a container object Combination mode Its structure is shown in Figure 4.

 

Dual distribution mode:

The visitor design pattern uses dual distribution. The principle is as follows:

Double dispatch

What is double distribution?

When talking about object-oriented programming, we often talk about object-oriented "polymorphism". Among them, there is often a saying that "parent class reference points to child class objects".

The reference of the parent class to the child class object is written in the following way:

Animal animal = new Dog(); 
animal.bark(); 

Another common form is

public class Keeper { 
  
     public void say(Animal a) { 
         System.out.println("Animal say"); 
     } 
  
     public void say(Dog dog) { 
         System.out.println("dog say"); 
     } 
} 
Animal animal = new Animal(); 
Animal dog = new Dog(); 
Keeper keeper = new Keeper(); 
keeper.say(animal); 
keep.say(dog);
--------
Copyright notice: This article is CSDN Blogger「M Ah」Original articles, follow CC 4.0 BY-SA Copyright agreement, please attach the original source link and this statement.
Original link: https://blog.csdn.net/yupi1057/article/details/80961871

What will be output when the above keeper calls say twice? Will two different methods be called?

In fact, the say(Animal a) method will be called in both calls. Because these contents can be confirmed at compile time, this is the static distribution of Java.

From the above figure, we can see that the bytecode generated by the two calls does point to the say(Animal a) method. The runtime directly executes the method and outputs the corresponding content.

Why does the corresponding animal.bark() eventually call the method of the dog class? This is to determine the type of specific method receiver and execute it at run time. This is called dynamic distribution, which determines the specific method at run time to realize object-oriented polymorphism.

Dispatch

Distribution refers to the process of finalizing a method to be executed.

For static languages such as Java, they are executed through single dispatch.

For example, such a line of code
 

dog.eat(new Bone())

The final execution of the eat method to be selected will only select the corresponding method according to the specific type of dog, and the passed in parameters will not affect the selection of the corresponding method. This is single Dispatch

In order to make the real parameters passed in, here is the Bone to really work, you need to use Double Dispatch or Multiple Dispatch

In other words, the final decision of which method to call is determined not only by the receiver of the method, but also by the parameter type.

Visitor mode

In the GoF design pattern, the Visitor pattern uses Double Dispatch to call real objects.

For the visitor pattern, the most common example is tree traversal. For example, there are differences in the processing of nodes and leaves. Through the dual distribution of visitors, different contents can be executed for different elements.

The code is similar to this:

node.accept(new ConcreateVisitor()); 
leaf.accept(new ConcreateVisitor()); 

The accept method in node will pass its real type back to visitor again

public void accept(Visitor v) { 
    v.visit(this); 
}

At this time, in the visitor, you can call specific methods according to the real type. There are similar methods corresponding to node and leaf:

public void visit(Node n); 
public void visit(Leaf l); 

To sum up, visitors generally include the visitor interface, which contains the processing logic of each Element object to be accessed. In the specific implementation of each Element, its own type is passed back to the visitor for secondary distribution to realize the exact logic call.


Reference article:

Detailed explanation of Visitor mode

Dual distribution of Java and Visitor mode_ yupi1057 blog - CSDN blog 

Posted by syamswaroop on Sun, 31 Oct 2021 03:32:48 -0700