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. Or, it might uses a generic method to return objects of any type.

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:

public interface TimeSource {

  /** Return the system time. */  
  long currentTimeMillis();

} 

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

public final class TimeSourceOneDayAhead implements TimeSource {

  /** One day in advance of the actual time.*/
  @Override public long currentTimeMillis() {
    return System.currentTimeMillis() + ONE_DAY;
  }
  
  private static final long ONE_DAY = 24*60*60*1000;

} 

Finally, here's a sketch of the Plugin Factory itself. In this case, a generic method is used to return concrete implementations.

import java.lang.reflect.InvocationTargetException;
import java.util.LinkedHashMap;
import java.util.Map;

/** 
 Return concrete implementations of interfaces.
 
 Here, each concrete implementation class needs to have a 
 no-argument constructor, in order to be built by this 
 factory.
*/
public final class PluginFactory {
  
  /** Simple demo. */
  public static void main(String... args) throws Exception {
    Map<String, String> config = new LinkedHashMap<>();
    //hard-coded, instead of using a text file as config: 
    config.put(TimeSource.class.getName(), TimeSourceOneDayAhead.class.getName());
    PluginFactory.init(config);
    
    TimeSource ts = PluginFactory.instanceOf(TimeSource.class);
    System.out.println(ts);
  }
  
  /**
   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 (where package names
   have been added):
   myapp.TimeSource = myapp.TimeSourceOneDayAdvance
   
   @param config map-key is the fully-qualified interface name, map-value is 
   the fully-qualified name of a corresponding concrete implementation class, 
   having a no-argument constructor.
   The caller decides where this data comes from. It may be a simple text file, a 
   database, etc. 
  */
  public static void init(Map<String, String> config){
    /*
     for testing (outside of production), you might want to let the caller 
     call this method more than once, to swap around different implementations. 
    */
    //interfaceToImpl.clear();
    
    interfaceToImpl.putAll(config);
    
    //variation: you might want to fail as early as possible, by trying to 
    //build an object here for each interface, as a simple test 
    
    //variation: you might want to allow the mapping to be set in code, instead of 
    //in a text file. In that case, the Map would use Class objects directly, and 
    //not their names.
  }
  
  /**
   Return an object that implements the given interface.
   If the given interface has no known mapping defined by the config, 
   or if the instance cannot be created, then an exception is thrown.
   
   Example of getting an instance that implements the TimeSoure interface: 
   <code>TimeSource ts = PluginFactory.instanceOf(TimeSource.class);</code>
   
   @param aInterface the class object representing the interface. 
  */
  public static <T> T instanceOf(Class<T> aInterface) throws 
    ClassNotFoundException, InstantiationException, IllegalAccessException, 
    IllegalArgumentException, InvocationTargetException, NoSuchMethodException, 
    SecurityException
  {
    T result = null;
    for(String interfaceName : interfaceToImpl.keySet()) {
      if (interfaceName.equals(aInterface.getName())) {
        String implName = interfaceToImpl.get(interfaceName);
        //unavoidable cast!
        Class<? extends T> implClass = (Class<? extends T>)Class.forName(implName);
        result = implClass.getDeclaredConstructor().newInstance();
      }
    }
    if (result == null) {
      throw new InstantiationException(
        "The interface " + aInterface.getName() + " has no mapping to an impl."
      );
    }
    return result;
  }
  
  // PRIVATE
  
  /**
   Maps the name of an interface to the name of a corresponding concrete 
   implementation class. 
  */
  private static final Map<String, String> interfaceToImpl = new LinkedHashMap<>();
} 

See Also :
Construct Object using class name
Use a fake system clock
Abstract Factory