The previous post shows how to implement a very simple and static approach to create Singletons using a static “Singleton” class. Now it is time to refactor our solution and make it more flexible.
First, I want to get rid of the static context. Static is very simple but also restrictive and we want and need flexibility later on. So instead of using static variables the get-method should delegate to the real implementation of the Singleton. How does this change our code?
[sourcecode language='java']public abstract class S {
private static final S SINGLETON; //???
@SuppressWarnings(”unchecked”)
public static
return SINGLETON._get(clazz);
}
S() {
// make Singleton accessible only through static references
}
abstract
}[/sourcecode]
Instead of static, the Singleton class (I renamed it to just “S” because of shortness) is now abstract. It is still the entry point for getting an instance, but now delegates it to an implementation of S. So it is possible to use different types of S. For now this gives us no real advantage, but in the next parts of the tutorial you will see how this helps us. Please also note the use of Java’s default visibility. When S has its own package, rest of the application isn’t aware of the _get-method. To use S, we need an implementation of it:
[sourcecode language='java']public class HashMapSingleton extends S {
private final HashMap
new HashMap
HashMapSingleton() {
super();
singletons.put(MyPrinter.class, new MySoutPrinter());
}
@Override
// TODO check for null values
return (T) singletons.get(clazz);
}
}[/sourcecode]
S now could just create an instance of HashMapSingleton and then delegate the requests to it. But we want a more flexible solution. I personally see no need for supporting runtime exchange, (too flexible, although it is possible) and since another goal is simplicity, the easiest way is deciding at startup which Singleton implementation to use. I am not a mind reader, but I guess the chance that you think ‘Here comes the configuration file!’ is pretty big. Not so fast, simple also means no configuration files. Therefore, we are using plain, oldschool Java System properties to determine which implementation to use. Adding the following lines should do the trick.
[sourcecode language='java']static {
String singletonClassName = System.getProperty(”com.hemju.S”);
S singleton = null;
try {
singleton = (S) Class.forName(singletonClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
SINGLETON = singleton;
}[/sourcecode]
When we start our program with the VM argument -Dcom.hemju.S=com.hemju.HashMapSingleton, everything should just work fine. One more thing bothers me. Can you recall why we did introduce S in the first place? Exactly, because a class should do only serve one purpose. HashMapSingleton does managing and creating the Singletons and latter is clearly not its responsibility. Therefore, we introduce another method in S that allows registering Singleton instances.
S
[sourcecode language='java']public abstract
HashMapSingleton
[sourcecode language='java']@Override
public
// TODO check for null values
singletons.put(clazz, impl);
}[/sourcecode]
Now it is possible for other classes to register Singletons. Wait, no static register? We don’t need one, we just register HashMapSingleton as a Singleton for S and S.get(S.class) gives us exactly what we want.
I think this solution is already better than the previous one, but there are still issues to be addressed which we will do in the next part.
