Pluggable Factory
Gw
The Search and Query group today identified that the Pluggable Factory design pattern is applicable to their system. When looking at the requirements for the Rendering team, I realized that it it would fit their needs too. (Note that I'm not entirely sure that what I'm going to present is, stricly speaking, actually Pluggable Factory, but I've seen it called that.)
Let's say you have a HandlerFactory that, given a String, will provide you with an instance of some implementation of an interface, let's say Handler. This would for example be useful for the Rendering team: the file type would be the key, the Handler interface would expose the functionality to render a page. This is, if I'm not mistaken, called Abstract Factory. A disadvantage of the Abstract Factory is you need fill HandlerFactory's String->Handler map with the appropriate data yourself; there has to be a piece of code where all implementations are listed and added to the map. This is unfortunate: if you add a new implementation, you not only need to code that implementation, but you also need to
edit this central list.
Pluggable Factory remedies this. Well, it should. It does in C++, but I haven't been able to get it to work in Java --and this seems to be a limitation of Java. But I am no Java expert and I haven't tried all that hard, so I'll present the basic idea here: maybe someone can get it to work and at the very least you'll have an interesting read ;).
The basic idea is to be able to write
HandlerFactory.get( "Foo" ).handle();
The implementations of HandlerFactory and Handler are pretty straightforward (I'm not doing proper checking):
class HandlerFactory {
public static Map handlers;
public static void add( String key, Handler h ) {
if( handlers==null ) handlers = new TreeMap();
handlers.put( key, h );
}
public static Handler get( String key ) {
return (Handler)handlers.get( key );
}
}
interface Handler {
// just any interface you need. simple example here.
public void handle();
}
And now to demonstrate the trick, we will write FooHandler, an implementation of Handler. FooHandler will itself contain a static FooHandler (this is no problem: it is static). The constructor of FooHandler will add an appropriate entry into HandlerFactory's map.
class FooHandler implements Handler {
// the actual trick:
// have a static instance of this class and have the constructor add
// this instance to the HandlerFactory map.
// too bad we have to repeat this for every Handler implementation.
public static FooHandler instance = new FooHandler();
FooHandler() {
HandlerFactory.add( "Foo", this );
}
// actual implementation of the interface
public void handle() {
System.out.println("FooHandler.handle()");
}
}
And bingo, we now have a class that automatically registers itself! Not really. Though this works in C++ (where all statics are initialized before the program `starts'), it doesn't in Java. In Java, statics seem to be initialized only when they are first used. The following code crashes:
public static void main( String[] args ) {
HandlerFactory.get( "Foo" ).handle();
}
This is because FooHandler.instance never gets initialized because it is never used. The following code works, but reintroduces the problem we were trying to solve: having to list all implementations from a central location.
public static void main( String[] args ) {
// need to touch the static to get it to initialize.
// well, now we've lost all we've been trying to gain :(.
FooHandler.instance = FooHandler.instance;
// would like to be able to write this without the above statement.
HandlerFactory.get( "Foo" ).handle();
}
We have achieved absolutely nothing. Only some funny code that doesn't do anything. If only we could get the statics to initialize themselves...
Regards,
Thomas
p.s.: if anyone has anything to say about this, i propose you add to this page, thread-style.
--
ThomasVanDijk - 16 Sep 2004
The difference from C++ regarding static initialization is that Java doesn't load all classes at once when the program execution starts (remember that every Java class has its own .java and .class files). Instead, classes are loaded as they are needed, at which point their statics are initialized.
Fortunately, there is a way around this 'issue': load the class dynamically. Using the static function
java.lang.Class.forName() (see
the docs for details), you can load a class, given its name. Of course, you still need to know the name of the classes, but perhaps you can figure them out by either retrieving a directory listing or even a configuration file listing all of them. I think that this solution might be a bit overkill though.
I have also found a site giving a few examples of this
here.
--
OlafJansen - 16 Sep 2004
Like Olaf said, it is a problem because of classloading. static variables are saved in the .classfile itself (contantpool afaik). The solution of Olaf is more like a workaround like he said. Not very nice, because if you have a key, you already should know the classname and you can solve it in your code itself.
By the way, how do you make sure that two object are not using the same key and if so, what can you guarantee?
Also remeber that, not like
AbstractFactory? , that your Factory will only return the static object en will not return a
new object. So with your factory you will always get the same instance.
My option: just enclose the strings in the logic of the
FactoryInterface? as consts (static final) if you want to create new objects or return static objects.
Last remark: you code could be so much nicer with Generics

--
JohnVanSchie - 16 Sep 2004