Thursday, March 20, 2014

Java InstanceOf Alternative with Enum

Hello,

Everyone deals with the instanceof problem in their Java (or is in C#) career. Instanceof isn't just considered wrong by most authors and programmers, but is aesthetically unpleasant.

So many programmers move to design patterns and the Visitor or Composite pattern. While perfectly valid solutions, there is another obvious solution not to be overlooked, particularly when polymorphism fails.

For the Libgdx game framework, I wanted to create a traditional observer - observable class hierarchy independent of the Libgdx framework. For example, for each type of event (a key pressed, the screen touched) I wanted a workflow to create that event, assign observers to it and be done with it.




These events descend from an AbstractEvent object which itself is a subclass of observable. They will be populated by an InputProcessor and GestureListener with the appropriate properties (if you haven't used Libgdx and want to make cross platform Java games I definitely recommend it).

When the Libgdx framework detects an event, it calls an object with the InputProcessor or GestureListener interface to handle the events. However, since I'm creating a class hierarchy outside Libgdx, I needed a way to populate these InputXXXX and GestureXXXX objects dynamically at runtime. For example, if the user pressed the screen, I wanted an object of InputTouchDown populated by Libgdx.

This InputTouchDown will already have been created and have its observers assigned. The issue however is I don't add InputTouchDown instances to the queue. I add AbstractEvent objects to the queue with a method signature like this :

public InputProcessor add(AbstractInput input) {  

So, how exactly does the add method know what is being added? The add method needs to know what is being added because each input event has different properties. For example, some events have x and y coordinates, some have counts, some have buttons. The add method then adds the AbstractInput to the appropriate instance variable (for example, an InputKeyUp instance) so that when an event happens the correct methods are called at runtime by Libgdx to populate it.



As you can see the problem is how to set the inputTouchDown when an AbstractInput is passed to the add method without resorting to instanceof? And even worse (though I don't have to deal with it in the Libgdx framework) how would I know when to call touchDown?

I could either push all the properties of the children into the parent (and pollute the class hierarchy) or find a way to deal with it.  The obvious solution was Visitor or Composite pattern. However, Visitor pattern seemed inadequate. Not only was it a limited number of visits (once every user initiated action) but it is a large number of events. The composite pattern made even less sense, because events are not "composed" of other events.

 


The answer was an enum. Here is the process.

1. Create a final variable in the superclass of the enum's type

2. Create a constructor in the superclass which requires the enum type as a parameter.



Now in the inherited classes, you do not need to put the enum in your constructors. Simply call super with the appropriate enum type within the constructor (do not expose this implementation).



Now a concrete instance variable assigned to an AbstractXXXX instance variable can always determine its correct type without instanceof, perhaps for casting or other runtime operations.

If you find that there are a limited number of subclasses possible for your class hierarchy (usually because you do not control the class hierarchy such as when building adapters for a third party framework), and you dislike the bloat of Visitor or the Composite pattern doesn't make sense, consider using this enum method. It probably violates every principle of object oriented design, but it keeps the codebase simple to debug and intuitive. You will still have the switch statement using getInputType but with aesthetically pleasing enums instead of instanceof calls.

Comments welcome below.