There are a million options. For example, you can create a factory base class that also has static methods for managing registered factories (unverified code printed here, sorry for the errors):
public abstract class CodecFactory { private final String name; public CodecFactory (String name) { this.name = name; } public final String getName () { return name; }
Then:
public class QuantumCodecFactory extends CodecFactory { public QuantumCodecFactory { super("quantum"); } @Override public Codec newInstance (String filename, String args) { return new QuantumCodec(filename, args); } }
Of course, this means that at some point you should:
CodecFactory.registerFactory(new QuantumCodecFactory());
Then use:
Codec codec = CodecFactory.newInstance(filename, "quantum", args);
Another option is to use reflection and maintain Map<String,Class<? extends CodecInterface>> Map<String,Class<? extends CodecInterface>> , using Class.newInstance() to instantiate. This is convenient to implement because it runs on top of the Java Class , which already supports a stylish factory model for creating objects. Cautions, as above, classes must be explicitly registered, and also (unlike above) you cannot implicitly use constructor parameter types at compile time (although you could at least abstract it by some method instead of directly calling Class.newInstance() from client code).
For instance:
public final class CodecFactory { private static final Map<String,Class<? extends Codec>> classes = new HashMap<String,Class<? extends Codec>>(); public static void registerClass (String name, Class<? extends Codec> clz) { classes.put(name, clz); } public static Codec newInstance (String filename, String codec, String args) { Class<? extends Codec> clz = classes.get(codec); if (clz != null) return clz.getDeclaredConstructor(String.class, String.class).newInstance(filename, args); else throw new IllegalArgumentException("No such codec."); } }
Where each Codec must have a constructor that accepts (String filename, String args) . Thus, registration:
CodecFactory.registerClass("quantum", QuantumCodec.class);
Usage is the same as above:
Codec codec = CodecFactory.newInstance(filename, "quantum", args);
You can even leave the card and simply use Class.forName() - this does not give you such flexibility with codec names, but essentially allows the class loader to do all the work for you, and you do not need to explicitly register the types ahead of time.
Edit: Re: Question in the comments below. You could come up with a system that combines the two above examples to create a universal factory based on reflection factory derived from CodecFactory , which still leaves you the opportunity to create other more specialized factories, for example:
public class GenericCodecFactory extends CodecFactory { private final String name; private final Class<? extends Codec> clz; public GenericCodecFactory (String name, String clzname) { this.name = name; this.clz = Class.forName(clzname); } public GenericCodecFactory (String name, Class<? extends Codec> clz) { this.name = name; this.clz = clz; }
Then you can use this for anything:
// you can use specialized factories ClassFactory.registerFactory(new QuantumCodecFactory()); // you can use the generic factory that requires a class at compile-time ClassFactory.registerFactory(new GenericCodecFactory("awesome", AwesomeCodec.class)); // you can use the generic factory that doesn't need to have class present at compile-time ClassFactory.registerFactory(new GenericCodecFactory("ninja", "com.mystuff.codecs.NinjaCodec"));
As you can see, there are many possibilities. Using Class.forName() in reflection-based factories is good because the class should not be present at compile time; therefore, you can put codec classes in the class path and, say, specify a list of class names in the runtime configuration file (then you can have a static ClassFactory.registerFactoriesListedInFile(String confgFilename) or something else) or scan the plugin directory. You can even create class names from simpler strings if you like it, for example:
public class GenericPackageCodecFactory extends GenericCodecFactory { public GenericPackageCodecFactory (String name) { super(name, "com.mystuff." + name + ".Codec"); } }
You can even use something like this as a backup in ClassFactory if the codec name is not found, to get ClassFactory to explicitly register types.
The difference seems to continue to arise because it is very flexible and the Class interface is essentially an all-encompassing factory class, so it is often parallel to what specific factory architectures are trying to implement.
Another option is to use the second example mentioned above (with Map<String,Class> ), but make a version of registerFactory that accepts the class name String instead of Class , similar to the general implementation I just mentioned. This is probably the smallest amount of code required to not create instances of CodecFactory s.
I cannot give examples for each combination of things that you can do here, so here is a partial list of the tools available to you that you should use as you wish. Remember: Factories are a concept; You need to use the tools that you must implement in this concept, according to your requirements.
- Reflection (
Class<?> And Class.forName ) - Static initializer blocks (sometimes this is a good place to register a factory; requires the class to be loaded, but
Class.forName can call this). - External configuration files
- Plugins like http://jpf.sourceforge.net/ or https://code.google.com/p/jspf/ or https://code.google.com/p/jin-plugin/ (good OSGi comparison , JPF, JSPF can be found here ; I have never heard of jin-plugin before looking at the answers in the link).
- Maps of registered factories and / or the ability to use reflection to generate class names on the fly.
- Remember, if necessary, parallel cards and / or synchronization primitives for multi-threaded support.
- Lots of other things.
Also: do not go crazy, performing all these possibilities, if you do not need it; think about your requirements and decide on the minimum amount of work that you must do here to satisfy them. For example, if you need extensible plugins, one JSPF may be enough to satisfy all your requirements without having to do any of these jobs (I have not actually tested it, so I'm not sure). If you don’t need this “scan” plugin behavior, simple implementations like the examples above will do the trick.