Plugin Factory

It's often useful to be able to quickly and easily switch one implementation of a given feature with another. This is especially useful when writing unit tests for your code, but the technique isn't strictly limited to unit tests.

A plugin factory is one way of quickly swapping implementations. The general idea is to:

Using configuration of some kind (often simply a text file), the plugin factory knows which concrete implementation it's supposed to return to its caller.

It's important for your application to treat the Plugin Factory as the sole source for implementations of the corresponding interfaces. That is, the rest of your app is not supposed to have direct knowledge of the concrete implementations. The Plugin Factory is meant to keep that knowledge secret.

A plugin factory can have a number of methods defined, each returning an implementation of a specific interface.

A recurring theme in object programming is allowing old code to call new code. A Plugin Factory is simply another variation on that important theme.

Example

As an example, let's take the idea of a fake system clock. In this case, you want the current time to be defined centrally, in one place. You also want to be able to swap in various ways of defining the current time, either for testing or for other reasons.

Here's the interface:


package myapp;

/**
 Return the time in milliseconds from the Java epoch.
 When combined with a TimeZone, such a millisecond value can be 
 used to create a date-time value.
 
 Variation: one might decide to return a date-time object directly, 
 instead of a millisecond value.
*/
public interface TimeSource {
  
  long currentTimeMillis();
  
}
 

Here's an example of a particular concrete implementation of the above interface:


package hirondelle.jp.util;

public final class TimeSourceOneDayAdvance implements TimeSource {
  
  public long currentTimeMillis() {
    return System.currentTimeMillis() + MILLISECS_PER_DAY;
  }
  
  private static final int MILLISECS_PER_DAY = 1000*60*60*24;
}
 

Here's a caller that uses a concrete implementation, without knowing its underlying class:

package myapp;

/**
Use a concrete implementation of an interface, without being linked directly to the 
the implementing class.

The concrete implementation is known only to the PluginFactory class.
*/
public final class UseFakeSystemClock {
  
  public void doSomethingThatDependsOnTime(){
    TimeSource timesource = PluginFactory.getTimeSource();
    long currentTime = timesource.currentTimeMillis();
    System.out.println("Current millisecond value: " + currentTime);
  }

}
 

Finally, here's a sketch of the Plugin Factory itself. Note that you can add more methods to this class, each corresponding to a different interface.

package myapp;

import java.util.LinkedHashMap;
import java.util.Map;

/** Return concrete implementations for specific, known interfaces. */
public final class PluginFactory {
  
  /**
   Read in configuration data that maps names of interfaces to names of 
   corresponding concrete implementation classes. Called early upon startup, 
   before any implementations are needed by the rest of the program.
   
   <P>Example of a possible entry in such a config file :
   myapp.TimeSource = myapp.TimeSourceOneDayAdvance
  */
  public static void init(){
    //elided
    //perhaps a properties file is read, perhaps some other source is used
  }
  
  /*
  * Another variation: allow the caller to swap in different implementation classes
  * at runtime, after calling init. This allows testing code to swap in various 
  * implementations.
  */
  
  /**
   Return the concrete implementation of the TimeSource interface.
  */
  public static TimeSource getTimeSource() {
    String implClassName = fImplementations.get("myapp.TimeSource");
    TimeSource result = (TimeSource)buildObject(implClassName);
    return result;
  }
  
  // PRIVATE
  
  /**
   Map the name of an interface to the name of a corresponding concrete 
   implementation class. 
  */
  private static final Map<String, String> fImplementations = new LinkedHashMap<>();
  
  private static Object buildObject(String aClassName){
    Object result = null;
    try {
      //note that, with this style, the implementation needs to have a
      //no-argument constructor!
      Class implClass = Class.forName(aClassName);
      result = implClass.newInstance();
    }
    catch (ClassNotFoundException ex) {
      //elided
    }
    catch (InstantiationException ex) {
      //elided
    }
    catch (IllegalAccessException ex) {
      //elided
    }
    return result;
  }
}
 



See Also :
Construct Object using class name
Use a fake system clock
Would you use this technique?
Yes   No   Undecided   
© 2014 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on September 21, 2013.
Over 2,000,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -