An introduction to the Visitor pattern of the Java design pattern

  • 2020-04-01 03:42:27
  • OfStack

Visitor definition: an operation that ACTS on individual objects in an object group. It allows you to define new operations on these objects without changing them.

In Java, the Visitor pattern essentially separates the elements in the collection structure from the behavior of the operations on those elements.

Why use the Visitor pattern
Java's Collection(including Vector and Hashtable) is one of the most commonly used techniques, but Collection is like a big black dye VAT. Objects with distinct types disappear once they are put in and taken out. Then we must use If to judge, such as:


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.toString()+"f");
   else
      System.out.println(o.toString());
}

In the above example, we used instanceof to determine the type of o.

Obviously, the drawback of doing this is that the code If else If is cumbersome, so we can use the Visitor pattern to solve it.

How do I use the Visitor pattern

For the above example, we designed an interface visitor:


public interface Visitor
{
   public void visitCollection(Collection collection);
   public void visitString(String string);
   public void visitFloat(Float float);
}

In this interface, we put in the types of classes that we think Collection is possible.

With visitors, we need to be visited, and the visitors are each Element in our Collection, and we need to define an interface for these elements that can be accessed (access and access are interactive, only the visitors, if the visitors are not welcome, the visitors will not be able to access).

We define this interface, Visitable, to define an Accept operation, that is, to make each element of the Collection accessible.


public interface Visitable{
   public void accept(Visitor visitor);
}

Now that we have two interfaces, we need to define their Concrete class:

public class ConcreteElement implements Visitable
{
   private String value;
   public ConcreteElement(String string) {
      value = string;
   }
   //Define the exact content of accept and here is a simple call to
   public void accept(Visitor visitor) {
      visitor.visitString(this);
   }
}

Take a look at the visitor's Concrete implementation:


public class ConcreteVisitor implements Visitor
{
   //In this method, we implement a successful access to the elements of the Collection
   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.toString()+"f");
   }
}

In the visitCollection above, we implemented access to each element of the Collection, using only one judgment statement to determine whether it is accessible or not.

At this point, we have completed the basic architecture of the Visitor pattern.

The premise for using the Visitor pattern

The object type in the object group structure (Collection) rarely changes, which means that the identity type of the Visitor rarely changes, such as the type in the Visitor above, and if new operations need to be added, such as the new ConcreteElement2 ConcreteElement3 in our example, in addition to the ConcreteElement concrete implementation.

It can be seen that there is a premise to use the Visitor pattern. In the two interfaces Visitor and Visitable, ensure that the Visitor changes little and Visitable changes, which is the most convenient way to use the Visitor.

If the Visitor also changes frequently, that is, the object type in the object group changes frequently, the general recommendation is to define the operations individually in these object classes, but Java's Reflect technique solves this problem.


Related articles: