Writing in the front

  • Take notes on learning design patterns
  • Improve the flexible use of design patterns

Learning to address

www.bilibili.com/video/BV1G4…

www.bilibili.com/video/BV1Np…

Refer to the article

C.biancheng.net/view/1317.h…

Program source codeGitee.com/zhuang-kang…

19. Visitor pattern

19.1 Definition and characteristics of the Visitor pattern

The definition of Visitor pattern: ** Separates operations on elements of a data structure into separate classes that can add new operations on these elements without changing the data structure, providing multiple access methods for each element in the data structure. ** It separates the manipulation of data from the data structure, and is the most complex of the behavior class patterns.

The Visitor pattern is an object behavior pattern with the following major advantages.

  1. Good scalability. The ability to add new functionality to elements in an object structure without modifying them.
  2. Good reusability. Visitors can be used to define common functions throughout the object structure, thereby increasing the reuse of the system.
  3. Good flexibility. The visitor pattern decouples the data structure from the operations acting on it, allowing the set of operations to evolve relatively freely without affecting the system’s data structure.
  4. Consistent with the principle of single responsibility. The visitor pattern encapsulates related behaviors into a single visitor, making each visitor’s function relatively single.

The main disadvantages of the Visitor pattern are as follows.

  1. Adding new element classes is difficult. In the visitor pattern, for every new element class, a specific action is added to each specific visitor class, which violates the “open close principle.”
  2. Break encapsulation. Specific elements in the visitor pattern expose details to visitors, breaking the encapsulation of objects.
  3. It violates the dependency inversion principle. The visitor pattern relies on concrete classes, not abstract ones.

19.2 Structure and implementation of visitor pattern

19.2.1 Structure of the Visitor pattern

  1. Abstract Visitor role: Defines an interface to access a concrete element, with an access operation visit() for each concrete element class, whose parameter types identify the concrete element to be accessed.
  2. ConcreteVisitor role: Implements the individual access operations declared in the abstract visitor role, determining what a visitor should do when accessing an element.
  3. Abstract Element role: Declares an interface that contains the accept() operation, with the accepted visitor object as an argument to the Accept () method.
  4. ConcreteElement role: implements the accept() operation provided by the abstract element role. The method body is usually visit.visit (this). The ConcreteElement may also contain operations related to its own business logic.
  5. A role is a container containing element roles that provide methods for visitor objects to traverse all the elements in the container, usually implemented by aggregation classes such as List, Set, Map, etc.

19.2.2 Code implementation

Now there are many people who keep pets, let’s take this as an example, of course, pets are also divided into dogs, cats, etc., to feed the pets, the owner can feed, other people can also feed.

  • Visitor role: the person who feeds the pet
  • Specific visitor roles: host, others
  • Abstract element personas: Animal abstract classes
  • Specific elements role: pet dog, pet cat
  • Structure object role: host family

Person

package com.zhuang.visitor;

/ * * *@Classname Person
 * @DescriptionAbstract visitor interface *@Date2021/3/27 took *@Created by dell
 */

public interface Person {
    // Feed the pet dog
    void feed(Dog dog);

    // Feed the pet cat
    void feed(Cat cat);
}
Copy the code

Owner

package com.zhuang.visitor;

/ * * *@Classname Owner
 * @DescriptionSpecific visitor role host class *@Date2021/3/27 now *@Created by dell
 */

public class Owner implements Person {
    @Override
    public void feed(Dog dog) {
        System.out.println("Owner feeds pet dog...");
    }

    @Override
    public void feed(Cat cat) {
        System.out.println("Owner feeds pet cat..."); }}Copy the code

Someone

package com.zhuang.visitor;

/ * * *@Classname Someone
 * @DescriptionSpecific visitor role other human *@Date2021/3/27 now *@Created by dell
 */

public class Someone implements Person {
    @Override
    public void feed(Dog dog) {
        System.out.println("Other people feed pet dogs...");
    }

    @Override
    public void feed(Cat cat) {
        System.out.println("Other people feed pet cats..."); }}Copy the code

Animal

package com.zhuang.visitor;

/ * * *@Classname Animal
 * @DescriptionDefine the abstract node *@Date2021/3/27 thus * were@Created by dell
 */

public interface Animal {
    void accept(Person person);
}
Copy the code

Dog

package com.zhuang.visitor;

/ * * *@Classname Dog
 * @DescriptionThe specific node implements the Animal interface *@Date2021/3/27 took *@Created by dell
 */

public class Dog implements Animal {
    @Override
    public void accept(Person person) {
        person.feed(this);
        System.out.println("Smells good, woof woof woof!!"); }}Copy the code

Cat

package com.zhuang.visitor;

/ * * *@Classname Cat
 * @DescriptionDescribe the purpose of a class in one sentence@Date2021/3/27 now *@Created by dell
 */

public class Cat implements Animal {
    @Override
    public void accept(Person person) {
        person.feed(this);
        System.out.println("Smells good, meow, meow, meow!!"); }}Copy the code

Home

package com.zhuang.visitor;

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

/ * * *@Classname Home
 * @DescriptionDefines the home of the owner of the object structure@Date2021/3/27 thus * were@Created by dell
 */

public class Home {
    private List<Animal> nodeList = new ArrayList<Animal>();

    // Add method
    public void add(Animal animal) {
        nodeList.add(animal);
    }

    public void aciton(Person person) {
        for(Animal node : nodeList) { node.accept(person); }}}Copy the code

Client

package com.zhuang.visitor;

/ * * *@Classname Client
 * @DescriptionVisitor pattern test classes *@Date2021/3/27 thus * were@Created by dell
 */

public class Client {
    public static void main(String[] args) {
        Home home = new Home();
        home.add(new Dog());
        home.add(new Cat());

        Owner owner = new Owner();
        home.aciton(owner);
        System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = =");
        Someone someone = newSomeone(); home.aciton(someone); }}Copy the code

19.3 extensions

The visitor pattern uses a double dispatch technique.

1. Dispatch:

The type in which a variable is declared is called the static type of the variable. Some people call static types obvious types. The actual type of the object referenced by a variable is called the actual type of the variable. For example, Map Map = new HashMap(), where the static type of the Map variable is Map and the actual type is HashMap. The choice of method based on the type of object is Dispatch, which is divided into two types: static Dispatch and dynamic Dispatch.

Static Dispatch occurs at compile time, and Dispatch occurs based on Static type information. Static dispatch is not new to us, and method overloading is static dispatch.

Dynamic Dispatch occurs at run time and dynamically swaps out a method. Java supports dynamic dispatch through method rewriting.

2. Dynamic dispatch:

Dynamic dispatch is supported through method rewriting.

public class Animal {
    public void execute(a) {
        System.out.println("Animal"); }}public class Dog extends Animal {
    @Override
    public void execute(a) {
        System.out.println("I'm a dog..."); }}public class Cat extends Animal {
     @Override
    public void execute(a) {
        System.out.println("I'm a cat..."); }}public class Client {
   	public static void main(String[] args) {
        Animal a = new Dog();
        a.execute();
        
        Animal a1 = newCat(); a1.execute(); }}Copy the code

The result of the above code we should be able to directly say, it is not polymorphic! A run executes a method in a subclass.

The Java compiler does not always know what code is being executed at compile time, because the compiler only knows the static type of the object, not the real type of the object; Methods are called based on the real type of the object, not the static type.

3. Static dispatch:

Static dispatch is supported through method overloading.

public class Animal {}public class Dog extends Animal {}public class Cat extends Animal {}public class Execute {
    public void execute(Animal a) {
        System.out.println("Animal");
    }

    public void execute(Dog d) {
        System.out.println("I'm a dog...");
    }

    public void execute(Cat c) {
        System.out.println("I'm a cat..."); }}public class Client {
    public static void main(String[] args) {
        Animal a = new Animal();
        Animal a1 = new Dog();
        Animal a2 = new Cat();

        Execute exe = newExecute(); exe.execute(a); exe.execute(a1); exe.execute(a2); }}Copy the code

Running results:

This may come as a surprise to some, but why?

Overloaded methods are dispatched based on static types, and the dispatch process is completed at compile time.

4. Double dispatch:

The so-called double dispatch technique is to select a method based not only on the runtime differences of the message receiver, but also on the runtime differences of the parameters.

public class Animal {
    public void accept(Execute exe) {
        exe.execute(this); }}public class Dog extends Animal {
    public void accept(Execute exe) {
        exe.execute(this); }}public class Cat extends Animal {
    public void accept(Execute exe) {
        exe.execute(this); }}public class Execute {
    public void execute(Animal a) {
        System.out.println("animal");
    }

    public void execute(Dog d) {
        System.out.println("I'm a dog...");
    }

    public void execute(Cat c) {
        System.out.println("I'm a cat..."); }}public class Client {
    public static void main(String[] args) {
        Animal a = new Animal();
        Animal d = new Dog();
        Animal c = new Cat();

        Execute exe = newExecute(); a.accept(exe); d.accept(exe); c.accept(exe); }}Copy the code

In the code above, the client will Execute objects as arguments passed to the Animal the method called types of variables, here to complete the first dispatch, here is the way to rewrite, so is the dynamic dispatch, also is to perform the actual type of the method, at the same time also this passed as a parameter in, here is completed the second assignment, There are multiple overloaded methods in the Execute class, and the pass is this, which is the object of the actual type.

So far, we’ve seen how double dispatch works, but what does it do? Is can realize the method of dynamic binding, we can modify the above procedure.

The running results are as follows:

The essence of double dispatch to achieve dynamic binding is to prefix the overloaded method delegate with the overridden link in the inheritance system. Since overloading is dynamic, overloading is dynamic.

Write in the last

  • If my article is useful to you, please give me a click 👍, thank you 😊!
  • If you have any questions, please point them out in the comments section! 💪