Visitor mode
brief introduction
The purpose of visitor pattern is to encapsulate some operations imposed on some data structure element. Once these operations need to be modified, the data structure that accepts the operation can remain unchanged.
Intent: mainly separate data structure from data operation.
It mainly solves the problems of stable data structure and changeable operation coupling.
When to use: you need to perform many different and irrelevant operations on the objects in an object structure. You need to avoid these operations "polluting" the classes of these objects, and use the visitor pattern to encapsulate these objects into classes.
role
Visitor: an abstract visitor declares an access operation for each concrete element class ConcreteElement in the object structure. From the name or parameter type of the operation, you can clearly know the type of the concrete element to be accessed. The concrete visitor needs to implement these operation methods and define the access operations to these elements.
Concrete visitor: the concrete visitor implements each operation declared by the abstract visitor, and each operation is used to access a type of element in the object structure.
Element: an abstract element is generally an abstract class or interface. It defines an accept() method, which usually takes an abstract visitor as a parameter.
ConcreteElement (concrete element): the specific element implements the accept() method and calls the visitor's access method in the accept() method to complete the operation of an element.
ObjectStructure: an object structure is a collection of elements. It is used to store element objects and provides a method to traverse its internal elements. It can be implemented in combination with composite mode, or it can be a simple collection object, such as a List object or a Set object.
code
A group of objects Archer, rider and gunner are visited by different visitors (AttackVisitor and RetreatVisitor), and the results will be different. One is attack and the other is retreat
The class diagram in this example is as follows:
Soldier is the soldier interface. Three "soldiers" are implemented in this example
In this example, there are two visitor s. Used to handle
Add dependencies as shown in the following figure:
Code structure
Soldier interface
public interface Soldier { void accept(Visitor v); }
Soldier interface implementation class
public class Archer implements Soldier { @Override public void accept(Visitor v) { v.visit(this); } public String getArcher() { return "Archer"; } } public class Gunner implements Soldier { @Override public void accept(Visitor v) { v.visit(this); } public String getGunner() { return "Gunner"; } } public class Rider implements Soldier { @Override public void accept(Visitor v) { v.visit(this); } public String getRider() { return "Rider"; } }
Visitor interface
public interface Visitor { void visit(Gunner gunner); void visit(Rider rider); void visit(Archer archer); }
Visitor interface implementation class
public class AttackVisitor implements Visitor { @Override public void visit(Gunner gunner) { System.out.println(gunner.getGunner() + " Launch an attack"); } @Override public void visit(Rider rider) { System.out.println(rider.getRider() + " Launch an attack"); } @Override public void visit(Archer archer) { System.out.println(archer.getArcher() + " Launch an attack"); } } public class RetreatVisitor implements Visitor { @Override public void visit(Gunner gunner) { System.out.println(gunner.getGunner() + " Start retreating"); } @Override public void visit(Rider rider) { System.out.println(rider.getRider() + " Start retreating"); } @Override public void visit(Archer archer) { System.out.println(archer.getArcher() + " Start retreating"); } }
Test class
public class ApiTest { @Test public void testCommand(){ Soldier[] list = {new Gunner(), new Rider(), new Archer()}; RetreatVisitor retreat = new RetreatVisitor(); AttackVisitor attack = new AttackVisitor(); for (Soldier element : list) { element.accept(attack); } System.out.println(); for (Soldier element : list) { element.accept(retreat); } } }
summary
1. The benefits of using visitor are clear at a glance. When you need to modify the business logic of some elements, you only need to modify the corresponding operation function in the visitor class. For example, if you want to modify the logic of the Wheel, you only need to modify the visitor's visit(Wheel wheel) method.
2. Suppose we need to add another car element skylight, we just need to add a new interface in the visitor to handle the new element, while other elements can remain unchanged. Violation of the opening and closing principle.
3. When we need to add new business operations, we only need to add new specific visitors, and others can remain unchanged. Comply with the opening and closing principle.
Similarly, there are advantages and disadvantages, because the logic is in the visitor, and all visitors and elements are highly coupled. Similarly, the return type of the visit method needs to be designed elegantly. Otherwise, once the return type is modified in the later stage, the scope of influence will be wide, and all visitor interfaces and implementations will be affected. The visitor mode requires the visitor object to access and call the operation of each Element object, which means that the Element object must sometimes expose some of its own internal operations and internal states, otherwise it cannot be accessed by the visitor, which is contrary to the Demeter rule and the dependency inversion principle.
In general, the visitor mode has harsh conditions and complex structure, so it is not used very frequently in practical applications.