Use Interfaces and Reflection to Achieve Application Configurability at the Class/Object Level

Over the years I have seen a lot of questions from Java programmers that have to do with choosing between different types of objects based on some criteria, criteria that are not known at the time the code is written or built but instead needs to be determined at deploy-time or run-time. A recent example of this came up on the Eclipse newsgroups, where a programmer (and fellow Eclipse user) was asking how he could have two different classes with the same name and package in the same project. Apparently the application he was working on needed to have two different implementations of this class, one for "normal" serial operation and another for parallel operation.
Well, there are various ways to accomplish having two different versions of the same class (in fact, he had accomplished it using Ant) , but doing so was masking the real issue - that this application needed to make a choice between different implementations at some time after the code was written and/or built. That is where this little "mini-pattern" comes in.

[ Aside: Let me be clear about the word "pattern" and how I use it here. What I'm talking about is vaguely related to AbstractFactory (or FactoryMethod) and Strategy, though it does not have to be as comprehensive as the GoF definition of those Patterns. In other words, this is more of a pattern than a Pattern. ]

The idea is to use interfaces and Java reflection to allow your application to instantiate certain classes based on runtime configuration, instead of hard-coding instantiation of those classes.
Let's take a sample app that needs to use a Bar object. Let's also say that there are many different kinds of bar (FooBar, BazBar, etc.) and that all the different types of bar have the same public interface. Finally, let's say that the app needs to use one of those types of bars, but does not know at compile time which one it might be.

The solution involves the following steps:

  • Define an interface, called Bar, that contains the public methods all bars must implement. For example:

    public interface Bar {
    public void doSomething();
    }

  • Write the various types of implementors of the Bar interface. For example:

    public class FooBar implements Bar {
    public void doSomething() {
    // do foo stuff here...
    }
    }
    public class BazBar implements Bar {
    public void doSomething() {
    // do baz stuff here...
    }
    }
    It does not matter what packages those impementations are in or how they implement the Bar methods, just that they implement the Bar interface.

  • In the client code (the code that needs to use a Bar), instead of creating an instance like normal [such as new FooBar()], determine from some configuration mechanism what is the fully qualified name of the Bar class that should be instantiated. The configuration mechanism can be any number of things, depending on how your application is deployed and the environment in which it is running. One option is to use a System Property that defines the name of the Bar implementation class; another is to read a config file; a third is to get the class name from a database; a fourth option is....well, you get the picture. It does not matter how you choose to get the class name; what is important is that somehow the code that needs Bar services can determine, at runtime, the name of a class that implements the Bar interface.

  • Once the client code has determined which Bar impl class is desired, use Java reflection to instantiate the class. For example:

    Bar aBar;
    String barClassName = getBarImplClass();
    aBar = (Bar) Class.forName(barClassName).newInstance();

This example is the simplest case, where the impl classes are assumed to have no-argument constructors. The code gets a little more involved if the constructor needs an argument/arguments (when using reflection like this I usually tend to strive for no-arg constructors and use a public initialize() method if data needs to be passed to the instance to initialize it - that method can be made part of the interface, which makes calling it easier once you have a reference to a Bar instance).

line
All photographs and content is copyright © 2003-2006 Eric Rizzo & Jazmine Rizzo

contact classes office hours about me Go home