This part will be a short one because I have to pack for my vacation. Yes, I will leave this cold and miserable weather, heading south to fill up my energy again (I am running on solar power).
Last time we switched from a static approach to an instance based one and introduced a register-method to allow adding Singletons from everywhere. There are several issues with this approach:
- doesn’t support lazy initialization, so far only eager initialization was done
- sometimes not all necessary information is available at register time to actually create the Singleton instance
- maybe you want to track, creation, requesting, and so forth of the Singleton.
Sounds complicated and scary? It isn’t, delegation is our friend again. Another way would be the observer (listener) pattern to address the above mentioned issues, however, delegation is more elegant and appropriate for this task.
Actually, the solution is as simple as it could be. We outsource (outsourcing in IT is a big thing!) the creation of Singletons to our new SingletonCreator interface. A SingletonCreator creates, like the name suggests, Singletons.
[sourcecode language='java']public interface SingletonCreator
T getInstance();
}[/sourcecode]
The neat thing is that it is up to the implementor how and when everything is done. E.g. eager/lazy initialization is defined by the implementation. But you don’t have to stop here. What about a time based Singleton? Only available through 8:00 to 17:00 from Monday to Friday (the unions are killing us)? Not a problem. You can also track Singleton instance requests or persist (serialize) them. Everything possible with SingletonCreator. So let us make the necessary changes to S and SingletonHashMap:
S.java
[sourcecode language='java']public abstract
SingletonHashMap.java
[sourcecode language='java']private final HashMap
new HashMap
HashMapSingleton() {
super();
// register as Singleton
register(S.class, new SingletonCreator() {
@Override
public S getInstance() {
return HashMapSingleton.this;
}
});
}
@SuppressWarnings(”unchecked”)
// checked via register!
@Override
// TODO check for null values
return (T) singletons.get(clazz).getInstance();
}
@Override
public
SingletonCreator
// TODO check for null values
singletons.put(clazz, creator);
}[/sourcecode]
SingletonHashMap already reveals how the new solution works, but isn’t the best example with all that clumpy syntax. Here is a better one, the Main class which registers our System.out and System.err printer:
[sourcecode language='java']package com.hemju;
public class Main {
public static void main(String[] args) {
// S.get(S.class).register(MyPrinter.class, new SerrPrinterCreator());
S.get(S.class).register(MyPrinter.class, new SoutPrinterCreator());
MyPrinter printer = S.get(MyPrinter.class);
printer.print(”Hallo aus Linz”);
}
private static class SerrPrinterCreator implements SingletonCreator
// eager init
private final MyPrinter printer = new MySerrPrinter();
@Override
public MyPrinter getInstance() {
return printer;
}
}
private static class SoutPrinterCreator implements SingletonCreator
private MyPrinter printer;
@Override
public MyPrinter getInstance() {
// lazy init
if (printer == null) {
printer = new MySoutPrinter();
}
return printer;
}
}
}[/sourcecode]
I am pretty satisfied with the solution by now. So what do you think about my Singleton solution? I attached the source code, so you can easily investigate the code.
One last word of cautions. Like most things in life nothing comes for free. Our new introduced flexibility holds certain pitfalls, or as we all know: With great power comes great responsibility. You don’t know what a SingletonCreator returns (null, a new instance every time, …). I will think about this possible problem, but until that I guess you have to trust your fellow programmers and of course yourself.
