Home > Misc > Visitor pattern to replace getting object metainformation from runtime

Visitor pattern to replace getting object metainformation from runtime

Assume we have different types of objects with a common interface in a single list. Objects in our example are instances of Circle, Rect and Polygon, which are all GeometricForms. In order to treat different types of objects in the list differently people often use runtime based mechanisms like instanceof (reflection), which get object metainformation from the runtime:

package com.geekoverdose.visitortest;

import java.util.ArrayList;
import java.util.List;

/**
 * Demo of using the visitor pattern to avoid instanceof statements - part 1.
 */
public class MainInstanceof {

    // ==============================================================================
    // CLASSES


    public interface GeometricForm {
    }

    public class Circle implements GeometricForm {
    }

    public class Rect implements GeometricForm {
    }

    public class Polygon implements GeometricForm {
    }
    
    // ==============================================================================
    // PROGRAM

    public void main() {
        // some list of objects that need to be drawn
        List<GeometricForm> list = new ArrayList<GeometricForm>() {
            {
                add(new Circle());
                add(new Rect());
                add(new Polygon());
            }
        };
        // separate by instanceof - can be very slow
        for (GeometricForm form : list) {
            if (form instanceof Circle) {
                // circle specific code here
                System.out.println("instanceof: circle");
            } else if (form instanceof Rect) {
                // rect specific code here
                System.out.println("instanceof: rect");
            } else if (form instanceof Polygon) {
                // polygon specific code here
                System.out.println("instanceof: polygon");
            } else {
                // error handling
            }
        }
    }
        
    public static void main(String[] args) {
        new MainInstanceof().main();
    }
}

A problem with asking metainformation from the runtime is the duration – which matters when the list is very long. In my original case we had hundreds of millions of geographic objects which needed to be drawn differently depending on object type and attributes. There using instanceof was not possible as it delayed drawing too much.

A way to avoid getting metainformation from the runtime is using the visitor pattern for our purpose. An abstract visitor provides different “visit” methods for different object types, so in our case one for Circle, Rect and Polygon. These methods get called if the visitor comes in contact with an object of the corresponding type. The common interface of your objects (GeometricForm) provides a single (abstract) method to “accept” a visitor. Each object type implements that method differently: so that the corresponding method for this object type get called at the visitor. So if we now want to process different object types differently we just call “visit” at the object and plug in a visitor which provides corresponding code for the different types, which can be created just in place:

package com.geekoverdose.visitortest;

import java.util.ArrayList;
import java.util.List;

/**
 * Demo of using the visitor pattern to avoid instanceof statements - part 1
 */
public class MainVisitorPattern {

    // ==============================================================================
    // CLASSES

    public interface GeometricFormVisitor<T> {
        // type parameter T to enable return value of any type when needed
        public T visit(Circle c);
        public T visit(Rect r);
        public T visit(Polygon p);
    }

    public interface GeometricForm {
        public abstract <T> T accept(GeometricFormVisitor<T> v);
    }

    public class Circle implements GeometricForm {
        @Override
        public <T> T accept(GeometricFormVisitor<T> v) {
            // automatically calls the Circle visit method
            return v.visit(this);
        }
    }

    public class Rect implements GeometricForm {
        @Override
        public <T> T accept(GeometricFormVisitor<T> v) {
            // automatically calls the Rect visit method
            return v.visit(this);
        }
    }

    public class Polygon implements GeometricForm {
        @Override
        public <T> T accept(GeometricFormVisitor<T> v) {
            // automatically calls the Polygon visit method
            return v.visit(this);
        }
    }

    // ==============================================================================
    // PROGRAM

    public void main() {
        // some list of objects that need to be drawn
        List<GeometricForm> list = new ArrayList<GeometricForm>() {
            {
                add(new Circle());
                add(new Rect());
                add(new Polygon());
            }
        };
        
        // visitor pattern - usually faster than using instanceof
        GeometricFormVisitor<Object> visitor = new GeometricFormVisitor<Object>() {
            @Override
            public Object visit(Circle c) {
                // circle specific code here
                System.out.println("visitor: circle");
                return null;
            }

            @Override
            public Object visit(Rect r) {
                // rect specific code here
                System.out.println("visitor: rect");
                return null;
            }

            @Override
            public Object visit(Polygon p) {
                // polygon specific code here
                System.out.println("visitor: polygon");
                return null;
            }
        };
        for (GeometricForm form : list) {
            Object someInformation = form.accept(visitor);
        }
    }

    public static void main(String[] args) {
        new MainVisitorPattern().main();
    }
}

(The type parameter T is a small extension that you can leave out if you do not need it. But if you at first need object type specific processing to extract information from an object, then process this information the same way for all objects this might be what you need).

Some more details on the problem: Polymorphism “fails” here, that’s why we use the workaround based on the visitor pattern. We cannot simply create one method each per object type, feed our object to those methods and let polymorphism solve the problem. The method call gets fixed at compile time, therefore to the supertype. So even if we have a method for our supertype and all subtypes it’s of no help. Good article describing the problem in more detail: https://sites.google.com/site/steveyegge2/when-polymorphism-fails

  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: