April 24, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Working With Design Patterns: Visitor

  • January 28, 2008
  • By Jeff Langr
  • Send Email »
  • More Articles »

Suppose you want to navigate an object composite to produce a corresponding XML document. The problem is best solved recursively: Transform the root object's primitive fields into simple elements; then, navigate into each non-primitive field and similarly transform it.

Your best tactic is to split the problem into two: navigation of the object hierarchy, and transformation of Java elements into corresponding XML elements. Navigating the object hierarchy involves use of the Java reflection API. Producing the XML involves simple text manipulation. To adhere to the single responsibility principle (SRP), your solution thus requires two separate classes: a navigator class and a transformer class.

The visitor pattern provides one conforming solution, and also presents opportunities for reuse. The pattern refers to something that will be navigated, or visited, as an object structure. In the XML example, the object structure is simply your Java object structure as accessed via reflection. The visitor contains logic to execute upon visiting each pertinent element in the object structure. In the XML example, the visitor is the transform logic.

Basically, the visitor pattern is organized as a callback structure. The object structure accepts a reference to a visitor interface. Your XML transformer is one implementation of the visitor interface, but you could supply alternate implementations for different needs. As logic in the object structure navigates through the Java object hierarchy, it calls back to a method (conventionally named visit) on the visitor object.

Listing 1 presents a visitor interface for the XML example. Not only does it represent the callbacks for visiting an object, but also for departure and a visit to each primitive attribute in a Java object. (To keep the example simple, I omit logic for iterating collections.)

Listing 1: A visitor interface.

import util.*;

public interface Visitor {
   void visit(HierarchyObject instance);
   void visitPrimitiveAttribute(HierarchyObject instance,
                                HierarchyField attribute);
   void depart(HierarchyObject instance);
}

The HierarchyWalker class (see Listing 2) provides the high-level policy for navigating Java objects. This is the object structure representation for the visitor pattern. The walk method takes the object to be navigated and the visitor callback. The supporting classes, HierarchyObject (see Listing 3) and HierarchyField (see Listing 4), encapsulate the mildly gory details of Java reflection.

First things first. The walker visits each instance:

   visitor.visit(instance);

The walk method then iterates through each primitive attribute on the passed object:

   for (HierarchyField attribute: instance.primitiveAttributes())
      visitor.visitPrimitiveAttribute(instance, attribute);

The code then walks through each non-primitive, recursively calling the walk method, for each:

    for (HierarchyField attribute:
      instance.nonPrimitiveAttributes())
      walk(attribute.getValue(object), visitor);

Finally, the walk method calls the depart method on the visitor.

   visitor.depart(instance);

Listing 2: HierarchyWalker.

import util.*;

public class HierarchyWalker {
   public void walk(Object object, Visitor visitor) {
      HierarchyObject instance = new HierarchyObject(object);
      visitor.visit(instance);
      for (HierarchyField attribute:
         instance.primitiveAttributes())
         visitor.visitPrimitiveAttribute(instance, attribute);
      for (HierarchyField attribute:
         instance.nonPrimitiveAttributes())
         walk(attribute.getValue(object), visitor);
      visitor.depart(instance);
   }
}

Listing 3: HierarchyObject.

import java.lang.reflect.*;
import java.util.*;

public class HierarchyObject {
   private Object object;

   public HierarchyObject(Object object) {
      this.object = object;
   }

   public String typeName() {
      return object.getClass().getSimpleName();
   }

   public Collection<HierarchyField> primitiveAttributes() {
      Collection<HierarchyField> attributes =
         new ArrayList<HierarchyField>();
      for (Field field: attributeFields())
         if (isPrimitiveOrString(field))
            attributes.add(new HierarchyField(field));
      return attributes;
   }

   public Collection<HierarchyField> nonPrimitiveAttributes() {
      Collection<HierarchyField> attributes =
         new ArrayList<HierarchyField>();
      for (Field field: attributeFields())
         if (!isPrimitiveOrString(field))
            attributes.add(new HierarchyField(field));
      return attributes;
   }

   private Field[] attributeFields() {
      return object.getClass().getDeclaredFields();
   }

   public Object getObject() {
      return object;
   }

   private boolean isPrimitiveOrString(Field field) {
      return field.getType().isPrimitive() ||
         field.getType() == String.class;
   }

   public String valueString(HierarchyField attribute) {
      return attribute.getValue(object).toString();
   }
}




Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel