preface

The visitor pattern is a behavioral design pattern. Behavioral design patterns are primarily concerned with the internal algorithms of objects and the assignment of responsibilities and responsibilities between objects, such as implementing abstractions such as algorithms, selection strategies, and state changes.

There are also two types of behavioral patterns.

  • Class behavior pattern: Use inheritance to relate behavior between classes.

  • Object behavior pattern: Use composition or aggregation to allocate behavior between different classes.

directory

A, definitions,

Encapsulates operations on the elements of a data structure. It defines new operations on the elements without changing the data structure.

The official definition, which you need to look at the code to understand at first, is to create a new class that encapsulates operations on other classes, such as fetching data from other classes, but does not change the data structure of other classes.

Two, mode principle analysis

First the template code, and then the example

Public abstract class Element{// define the business logic public abstract void doSomething(); Public abstract void accept(IVisitor visitor); } public void accept(IVisitor visitor){} public void accept(IVisitor visitor){ visitor.visit(this); }} public abstract ConcreteElement2 extend Element{public void Accept (IVisitor visitor){ visitor.visit(this); Public void visit(ConcreteElement1 el1); public void visit(ConcreteElement1 el1); public void visit(ConcreteElement2 el2); } public void visit(ConcreteElement1 el1){} public void visit(ConcreteElement1 el1){ el1.doSomething(); } public void visit(ConcreteElement2 el2){el2.dosomething (); }} Let's look at a simple example where different routes are used to access different operating systemsCopy the code

If you are a manufacturer of router software, and you have software requirements from many different hardware brands (e.g., D-Link, TP-Link), then you need to make the routers compatible with different operating systems (e.g., Linux and Windows) to send data

Public interface {void sendData(char[] data); void accept(RouterVisitor v); Public class DLinkRouter implements Router{@override public void sendData(char[] data) {} @Override public void accept(RouterVisitor v) { v.visit(this); } } public class TPLinkRouter implements Router { @Override public void sendData(char[] data) { } @Override public void accept(RouterVisitor v) { v.visit(this); Public Interface RouterVisitor {void Visit (DLinkRouter router); public interface RouterVisitor {void visit(DLinkRouter router); void visit(TPLinkRouter router); } public class LinuxRouterVisitor implements RouterVisitor{ @Override public void visit(DLinkRouter router) { System.out.println("=== DLinkRouter Linux visit success!" ); } @Override public void visit(TPLinkRouter router) { System.out.println("=== TPLinkRouter Linux visit success!" ); } } public class WindowsRouterVisitor implements RouterVisitor{ @Override public void visit(DLinkRouter router) { System.out.println("=== DLinkRouter Windows visit success!" ); } @Override public void visit(TPLinkRouter router) { System.out.println("=== DLinkRouter Windows visit success!" ); }} public Class Client {public static void main(String[] args) {LinuxRouterVisitor LinuxRouterVisitor = new LinuxRouterVisitor(); WindowsRouterVisitor windowsRouterVisitor = new WindowsRouterVisitor(); DLinkRouter dLinkRouter = new DLinkRouter(); dLinkRouter.accept(linuxRouterVisitor); dLinkRouter.accept(windowsRouterVisitor); TPLinkRouter tpLinkRouter = new TPLinkRouter(); tpLinkRouter.accept(linuxRouterVisitor); tpLinkRouter.accept(windowsRouterVisitor); } // Output === = DLinkRouter Linux visit success! === DLinkRouter Windows visit success! === TPLinkRouter Linux visit success! === DLinkRouter Windows visit success!Copy the code

Different types of routers can dynamically add (first dispatch) accept at runtime, and for different operating systems, routers can dynamically select (second dispatch) visit, which completes two dynamic bindings.

Single dispatch

  • Single-dispatch languages handle an operation based on the name of the requester and the parameters received. In Java, there is static binding and dynamic binding, and its implementation is based on overloading and overwriting.

  • Public class IdiotRole {} public class IdiotRole {} public class IdiotRole {} public class IdiotRole {} public class IdiotRole { Public class AbsActor{public void act(Role Role){// Actor can play all roles System.out.println(" Actors can play all roles "); } public void act(GongFuRole role){system.out.println (" young people like to play GongFuRole "); }} public class YoungActor extend AbsActor{public void act(GongFuRole role){system.out.println (" GongFuRole "); }} public class OldActor extend AbsActor{public void act(GongFuRole role){system.out.println (" old man can't play GongFuRole !!!!") ); } // AbsActor actor = new OldActor(); Role role = new GongFuRole(); actor.act(role); actor.act(new GongFuRole()); // The output actor can play all roles. Old people cannot play kung fu rolesCopy the code
  • Overloading is when the compiler decides which method to call. It calls act(role role) based on the apparent type of the role, which is static binding, whereas act(Actor) is dynamically bound based on its actual type.

Double dispatch

  • The resulting execution depends on the type of request and the types of the two recipients. Corresponding to theRouterThe type andVisitorThe type of.

Iii. Usage Scenarios

  • An object structure contains many classes that have different interfaces, and you want to perform operations on those objects that depend on their concrete classes.

  • Or you want to do a lot of different, unrelated operations on objects in an object structure, and you want to avoid “contaminating” the classes of those objects by those operations.

  • Or maybe you want to intercept some logic for different objects, different operations.

  • When data structures need to be separated from less common operations

Four advantages,

  • The open closed principle is satisfied because the visitor mode does not modify the original object, but adds external unified operations

  • Following the single responsibility principle, two subclasses of Router are responsible for loading and transferring the data, and two subclasses of RouterVisitor are responsible for configuring the specific data

  • Extensibility, if the router changes, or if some configuration changes are added to the router, it can be done in the two subclasses of Visitor

Five, the disadvantages

  • The concrete element exposes the details to the visitor, that is, the TPLinkRouter class needs to know the concrete implementation of the WindowsRouterVisitor, needs to know its methods and data, which violates Demeter’s law.

  • It is difficult to change specific elements, that is, it is easy to add one router, but how about adding many? Or does the RouterVisitor class have new attributes? Greatly altered.