Java TIP: Real Visitor mode with Reflection

zhaozj2021-02-11  215

Java TIP: Real Visitor mode with Reflection

Overview

The commonality of the Visitor mode is that it separates the structure of the object set and the operation performed by the set. For example, it can separate the analysis logic and code generation logic in a compiler. With this separation, it will be easy to use different code builders. More benefits, other utilities, such as LINT, can be tired of code generation logic while using analysis logic. Unfortunately, adding new objects to a collection often requires a modification of the Visitor class that has been written. This paper proposes a more flexible way to implement Visitor mode in Java: use reflection.

-------------------------------------------------- -----------

Collection is widely used in object-oriented programming, but it often triggers some questions related to code. For example, "If a collection is different objects, how do you perform actions?"

One way is to overlap each element in a collection, then perform a corresponding operation, respectively, for each element, based on the class. This will be difficult, especially if you don't know what type of objects in the collection. For example, suppose you want to print the elements in the collection, you can write a method (Method):

Public void messyprintcollection (Collection Collection) {iterator itrator = Collection.iterator () while (item.hasnext ()) System.out.println (item () ney (). Tostring ())}

This looks simple enough. It simply calls the object.tostring () method, then print the object, right? But what if there is a group of hash tables? Things will begin to become complicated. You must check the type of object returned from the collection:

public void messyPrintCollection (Collection collection) {Iterator iterator = collection.iterator () while (iterator.hasNext ()) {Object o = iterator.next (); if (o instanceof Collection) messyPrintCollection ((Collection) o); else System .out.println (o.tostring ());}}

Yes, now I have solved the problem of nested collection, but it needs to return String, if there is any other object that does not return String, what should I do? What should I do if I want to add quotation marks before and after the String object and add f after float? Code is still more and more complex:

public void messyPrintCollection (Collection collection) {Iterator iterator = collection.iterator () while (iterator.hasNext ()) {Object o = iterator.next (); if (o instanceof Collection) messyPrintCollection ((Collection) o); else if (o instanceof string) System.out.println ("'" o.tostring () "'); Else if (o instanceof float) system.out.println (o.toTString () " f "); Else System.Println (O.Tostring ());}} It can be seen that the complexity of things will grow sharply. You certainly don't want a code to flood the IF-ELSE statement everywhere! What is avoided? Visitor mode can help you.

To implement Visitor mode, you have to create a Visitor interface for the visitor, and build a Visitable interface for the accessed collection. Then, let the specific class implement Visitor and Visitable interfaces. These two interfaces are as follows:

Public interface visitor {public void visitcollection; public void visitstring (String string); public void visitfloat (float float);}

Public interface visitable {public void accept (visitor visitor);}

For specific String, it may be like this:

Public class visitableString IMPLEments VALUE; Public VisitablesTRING (String String) {Value = String;} PUBLIC VOID Accept (Visitor Visitor) {visitor.visitstring (this);}}

In the Accept method, call the correct Visitor method for the THIS type:

Visitor.visitstring (this)

In this way, you can implement specific Visitor as follows:

public class PrintVisitor implements Visitor {public void visitCollection (Collection collection) {Iterator iterator = collection.iterator () while (iterator.hasNext ()) {Object o = iterator.next (); if (o instanceof Visitable) ((Visitable) o) .accept (this);

Public void visitstring (string string) {system.out.println ("'" string "");}

Public void visitfloat (float float) {system.out.println (float.toT7tring () "f");}}

When implementing the VisitableFloat and the VisitableCollection class, they also call the appropriate Visitor method, the resulting effects, like the previously used IF-ELSE's MessyPrintCollection method, but the technique here is clearer. In VisitCollection (), calling Visitable.Accept (this), then this call returns to call a suitable Visitor method. This is called "double distribution"; ie, Visitor first calls the method in the Visitable class, and this method is called to the Visitor class.

Although the IF-ELSE statement is eliminated by implementing Visitor, there is also a lot of additional code. The initial String and Float objects are packaged with objects that implements a Visitable interface. This is a bit annoying, but generally is not a problem, because you can let the collections that are often accessed only contain those objects that implement the Visitable interface.

But it seems that this is still an extra job. Worse, what happens when adding a new Visitable type such as VisitableInteger? This is a major defect in Visitor model. If you want to add a new Visitable object, you must modify the Visitor interface and then implement one by one to each Visitor implementation. You can use a Visitor abstraction base class with a default air operation instead of the interface. Then I am very like an Adapter class in Java GUI. The problem is that it needs to take us inheritance; and you often want to keep a single inherit, let it use something else, such as inheriting StringWriter. That way is restricted, it can only be successfully accessed by Visitable objects.

Fortunately, Java can make Visitor models more flexible, allow you to add Visitable objects as you want. How to do it? The answer is to use the reflection. For example, you can design such a ReflectiveVisitor interface, which only needs one way:

Public interface reflectivevisitor {public void visit (Object O);

This is simple. As for Visitable, I have been saying again like front. Now use reflection to implement PrintVisitor:

public class PrintVisitor implements ReflectiveVisitor {public void visitCollection (Collection collection) {... same as above ...} public void visitString (String string) {... same as above ...} public void visitFloat (Float float) { ... Same as an above ...}

Public void default (Object O) {system.out.println (o.tostring ());

public void visit (Object o) {// Class.getName () returns package information as well // This strips off the package information giving us // just the class name String methodName = o.getClass () getName ()..; MethodName = "Visit" methodname.substring (MethodName.lastIndexof ('.') 1); // Now we try to invoke the method visit try {// get the method visitfoo (foo foo) Method m = getClass () .getMethod (MethodName, New class [] {o.getClass (); // Try to invoke visitfoo (foo foo) m.invoke (this, new object [] {o});} catch (nosuchmethodexception e) { // no method, so do the default importation default (o);}} Now no Visitable packaging class is now required. Just just calling Visit (), the request will be distributed to the correct method. Very nice, as long as it is suitable, Visit () can distribute. This is not necessary to use Reflection - it can use other completely different mechanisms.

In the new PrintVisitor, there are methods written for Collection, String and Float, but then it captures all unprocessed types in the CATCH statement. You want to extend the Visit () method so that it can also handle all parent classes. First, add a new method, called getMethod (Class C), it returns the method to be called; in order to find this method, look for all the parent classs of class C, then all in class C Search in the interface.

Protected method getMethod (Class C) {class newc = C; method m = null; // Try the superclasses while (m == null& newc! = Object.class) {string method = newc.getname (); method = " Visit " Method.Substring ('.') 1); try {m = getClass (). getMethod (Method, New class [] {newc});} catch (NosuchMethodeExcection) {newc = newc . Getsuperclass ();}} // Try the interfaces. if Necessary, You // CAN Sort The first to define 'Visitable' Interface Wins // In Case An Object Implements More One. if (NEWC == Object.class) {Class [] interfaces = C.GetInterface (); for (int i = 0; i

Note that in order to take care of the readers who are familiar with traditional Visitor mode, my name is the traditional naming method. But as you have noticed, name all the methods "VIT" and let parameter types will be more efficient. But if you do, you have to change the name of the main Visit (Object O) method to Dispatch (Object O). Otherwise, there is no default method available, you have to convert the type to Object when calling Visit (Object O) to ensure that the VISIT is the correct call mode.

You can now modify the Visit () method to use getMethod (): public void visit (object object) {try {method (), Object.getClass ()); method.invoke (this, new object [] {Object});} catch (Exception E) {}}

Now, the Visitor object is powerful. You can pass any object and have a method to handle it. Another benefit is that there is also a default method VisitObject (Object O) that captures any unknown objects. Take more work more, you can also write a VisitNull () method.

I am on the top of the Visitable interface without coming from coming from any reason. Another advantage of traditional Visitor mode is that it allows Visitable objects to control access to object structure. For example, suppose there is a TREENODE object that implements a Visitable, you can make an accept () method to traverse its left and right nodes:

Public void accept (visitor visitor) {visitor.visittreenode (this); visitor.visittreenode (leftsubtree); Visitor.Visittreenode (RightSubtree);}

In this way, only Visitor classes are used to modify the Visitor class, you can perform Visitable Control Access:

public void visit (Object object) throws Exception {Method method = getMethod (getClass (), object.getClass ()); method.invoke (this, new Object [] {object}); if (object instanceof Visitable) {callAccept ( (Visitable);}} public void callaccept (Visitable Visitable) {Visitable.Accept (this);}

If a Visitable object structure has been implemented, you can retain the Callaccept () method and use Visitable Control Access. If you want to access the structure in Visitor, just overwrite the Callaccept () method, make what you do.

Want to let several different visits accessed the same object collection, Visitor mode can play a powerful role. Assume that there is already an interpreter, a bluffer writing, a suffix writing, an XML Writer, and a SQL Writer, which act on the same object collection. Then, a prefix writing device and a SOAP Writer can be easily written for the same object. In addition, these writingers work normally and they don't know; of course, if they are willing, they can throw an exception.

Conclusion By using Java Reflection, you can enhance Visitor mode to make it powerful to operate the object structure and provide flexibility in adding new Visitable types. I hope that you can apply this mode in future programming.

转载请注明原文地址:https://www.9cbs.com/read-4436.html

New Post(0)