The article was first published on a personal blogshuyi.techWelcome to more interesting and valuable articles.
The visitor pattern, the emphasis is on the visitor. When we think of interviews, what inevitably comes to mind is a news interview, where two people sit across from each other. The visitor (a public figure) treats the visitor (a journalist) as an outsider and does not want you to move around. What do you want? I’ll give it to you when I’m done.
What is the Visitor pattern?
The visitor pattern is defined as follows, which defines new operations without changing the data structure.
Encapsulates operations that operate on elements of a data structure and defines new operations that operate on those elements without changing the data structure.
But in the actual application, I found that some examples are not so. Some examples don’t have stable data structures, but rather stable algorithms. In treeview, the visitor pattern is: the fixed, the open out of the change.
Let’s take an example from real life: a scientist gives a memory interview. We all know that when scientists receive interviews, there must be procedural restrictions. It is impossible for you to ask freely. Let’s say the process involves asking scientists about their school experience, then their work experience, and finally their research results. So what’s fixed in this process? Fixed is the interview process. What has changed? What changes is that different journalists may ask different questions about the school experience.
According to our previous understanding, the visitor model is to fix the invariable things and open up the changes. So this is an abstraction of the scientist being interviewed.
First, we need to have a Visitor class that defines things that outsiders (journalists) can do (ask questions about school experience, work experience, research achievements).
public interface Visitor {
public void askSchoolExperience(String name);
public void askWorkExperience(String name);
public void askScienceAchievement(String name);
}
Copy the code
Then declare a XinhuaVisitor class to implement the Visitor class, which indicates that a Xinhua reporter (Visitor) wants to visit a scientist.
public class XinhuaVisitor implements Visitor{ @Override public void askSchoolExperience(String name) { %s: what was your greatest accomplishment in school? \n", name); } @override public void askWorkExperience(String name) {system.out.printf (" what was the most memorable thing about your job? \n", name); } @override public void askScienceAchievement(String name) {system.out.printf (" %s: what is the biggest achievement?" , name); }}Copy the code
He is a Scientist. The scientist receives requests for access from journalists (visitors) through an Accept () method and stores them. Scientists have defined a method for interviews, where the process is set in stone, and I’ll only let you ask questions if you’re going to ask something.
public class Scientist { private Visitor visitor; private String name; private Scientist(){} public Scientist(String name) { this.name = name; } public void accept(Visitor visitor) { this.visitor = visitor; {} public void interview () System. The out. Println (" -- -- -- -- -- -- -- -- -- -- -- -- access to start -- -- -- -- -- -- -- -- -- -- -- -- "); System.out.println("-- start talking about school experience --"); visitor.askSchoolExperience(name); System.out.println("-- start talking about work experience --"); visitor.askWorkExperience(name); System.out.println("-- start talking about research results --"); visitor.askScienceAchievement(name); }}Copy the code
Finally, we declare a scene class Client to simulate the interview process.
Public class Client {public static void main(String[] args) {Scientist Yang = new Scientist(" Yang "); yang.accept(new XinhuaVisitor()); yang.interview(); }}Copy the code
The result of the run is:
-- -- -- -- -- -- -- -- -- -- -- -- access to start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start talking about school experience - Yang: excuse me what is the biggest accomplishment at school? -- Start talking about work experience -- Yang Zhenning: what is the most unforgettable advice on work? -- Start talking about scientific research achievements -- May I ask Yang Zhenning: what are the biggest scientific research achievements?Copy the code
This gives you a more intuitive understanding of the nature of the visitor pattern (fixing the unchanging and opening up the changing). In this case, what doesn’t change is the interview process, and what changes is that you can ask different questions.
In general, the class structure of the visitor pattern looks like this:
- Visitor Visitor interface. The visitor interface defines what visitors can do. This requires you to analyze what is mutable, to abstract that mutable content into a visitor interface, and to open it up. The information of the visitor is actually passed through the visitor’s parameters.
- ConcreteVisitor ConcreteVisitor. A concrete visitor defines the implementation of a specific type of visitor. Xinhua reporters, for their part, are more interested in Yang zhenning’s scientific achievements, so they tend to dig for results when they ask questions. But for the youth Daily reporters, their readers are teenagers, who are more concerned about Yang Zhenning’s spirit in study and work.
- Element Specifies the Element. This refers to the class being visited, in our case the Scientist class. In general, we will provide an Accept () method that accepts the visitor’s argument, which will be equivalent to accepting their sample application. But it’s not necessary, as long as you can get the visitor object, you can define the parameter pass however you want.
The most important classes for the Visitor pattern are Visitor, ConcreteVisitor, and Element. Visitor, ConcreteVisitor defines what a Visitor can do, and the parameters of the called are passed to the Visitor. Element gets its visitor object in a variety of ways, usually through the Accept () method, but not always.
It is important to note that our focus in studying design patterns is on understanding the relationships between classes and the information they convey. Whether it is passed through the Accept () method or through the constructor is beside the point.
02 Practical application of the Visitor pattern
We used a real life example to help you understand the visitor pattern. I believe you should have a sensible understanding of the visitor pattern. To get back to the programming practice itself, give you a better practical understanding of the visitor pattern. Let’s talk about the application of the visitor pattern in open source frameworks from a software programming perspective.
File tree traversal
We are aware that there are file operations in the JDK. If there are file operations, there will naturally be folder traversal operations, that is, access to all files or folders under a folder. Imagine if we wanted to print out the names of all the files and folders in a folder.
Very simple approach, in fact, is directly do a tree traversal, and then print out the name!
Yes, that’s the right answer!
So what if I want to count all the files and folders?
So go through it again, and then use a counter to keep adding one!
Yes, that’s the right answer too!
But did you notice that in both cases, we have the same operation: traversing the file tree. Whether it’s printing the file name, or calculating the file tree, we need to traverse the file tree. In either case, all we really want is access to the file.
Remember what we said about the nature of design patterns? The essence of design patterns is to find what doesn’t change, then find what changes, and then find the right data structures (design patterns) to accommodate that change.
In this case, what doesn’t change is the traversal of the file tree. What changes is the different access operations to the file. Obviously, the visitor pattern is a good place to accommodate this change. We can fix the immutable (traversal of the file tree) and open up the mutable (operation of the file). The JDK traverses the file tree using the visitor pattern.
The JDK declares a FileVisitor interface that defines what a traverser can do.
public interface FileVisitor<T> {
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs);
FileVisitResult visitFile(T file, BasicFileAttributes attrs)
throws IOException;
FileVisitResult visitFileFailed(T file, IOException exc)
throws IOException;
FileVisitResult postVisitDirectory(T dir, IOException exc)
throws IOException;
}
Copy the code
The visitFile() method defined in FileVisitor is simply access to files. The information for the called (file) is passed through the first parameter file. This allows the traverser to access the contents of the file.
SimpleFileVisitor is an implementation of the FileVisitor interface, which does simple parameter validation without much logic.
public class SimpleFileVisitor<T> implements FileVisitor<T> { @Override public FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException { Objects.requireNonNull(dir); Objects.requireNonNull(attrs); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException { Objects.requireNonNull(file); Objects.requireNonNull(attrs); return FileVisitResult.CONTINUE; } / /... Other ellipses}Copy the code
The FileVisitor and SimpleFileVisitor classes correspond to the Visitor and ConcreteVisitor classes in the UML class diagram. Element, on the other hand, corresponds to the Files class in the JDK.
WalkFileTree () is used to traverse the file tree in Files Files. In the walkFileTree() method to achieve tree traversal, traversal to the file will be through the visitFile method of visitor class call traverser’s method, traversal to the file passed to the traverser, so as to achieve the purpose of separation of changes.
ASM modifies the bytecode
ASM is Java’s bytecode enhancement technology, which uses the visitor pattern, mainly for bytecode modification. The three related classes in ASM are ClassReader, ClassVisitor, and ClassWriter.
The ClassReader class is equivalent to the Element in the visitor schema. It reads byte arrays or class files into memory and represents them as tree data structures. This class defines an Accept method to interact with the visitor.
ClassVisitor corresponds to the abstract visitor interface. After the ClassReader object is created, you call the Accept () method, passing in a ClassVisitor object. Different visit() methods in the ClassVisitor object are called at different times of the ClassReader to modify the bytecode.
ClassWriter is the implementation class of the ClassVisitor, which outputs the modified bytecode as a byte array.
For a scenario like ASM, the bytecode specification is very strict and stable, and can be problematic if changed arbitrarily. But we need to modify the bytecode dynamically to achieve something. In this case, ASM designers use the visitor pattern to isolate the changing parts and anchor the unchanging parts to achieve flexible scaling.
03 How should we use it?
From the examples above, we can get a sense of where the visitor pattern might be used: when something is stable (data structures or algorithms), but you don’t want to change it directly, but you want to extend the functionality.
When it comes to the definition of the visitor pattern usage scenario, the template method pattern looks very similar to this usage scenario. But they are slightly different. The variation and non-variation of the visitor pattern (that is, the visitor and the visitor) are simply inclusion relationships, whereas the variation and non-variation of the template method pattern is inheritance relationships. But they do have something in common, that is, both encapsulate the fixed and open up the mutable.
The benefits of the visitor pattern are obvious: it isolates what changes and fixes what doesn’t, making the whole more maintainable and scalable. However, it also introduces some disadvantages of common design patterns, such as:
- The class structure becomes complex. Instead of simple invocation relationships, we now have inheritance and composition relationships between classes. To a certain extent, it raises the requirements for developers and the cost of research and development.
- It becomes more difficult for the visitor to change. For example, in the example of the Scientist interview above, if a new link is expected to be added to the Scientist interview, the Scientist class needs to be modified, while the Visitor class and XinhuaVisitor class need to be modified.
With all of these advantages, but with all of these disadvantages, how do we decide whether to use the visitor pattern in real work?
The general principle is that the best time to use the visitor pattern is when the scenario fully exploits the advantages of the visitor pattern and avoids the disadvantages of the visitor pattern.
While using the visitor pattern makes it more difficult for the visitor to change, this disadvantage is eliminated if the visitor is stable and rarely changes. In the ASM example, for example, the element is ClassReader, which stores the structure of the bytecode. The bytecode structure doesn’t change easily at all, so the disadvantage of making it harder for visitors to change doesn’t exist.
The disadvantage of “complex class structures” depends on the complexity of the business at the time. If the business was simple and didn’t change much, then using design patterns was completely unnecessary. But if the business is complicated and we’re still making changes in a class, there’s a good chance something will go wrong. This is where design patterns are needed to support complex business structures.
04 References
- This article explains visitor model – Rhino Breeder blog
- Visitor Design Pattern
- One visitor pattern is enough. – Jane
- This article explains visitor model – Rhino Breeder blog
- Use of the Visitor pattern in the ASM framework
- Visitor pattern from shallow to deep and use case scenarios plus simple use of AMS _A1032722788 blog – CSDN blog