Do you remember the programming school and what the teacher was repeating any lesson? "Using global state is a bad programming technique!" Singletons are nothing more than doing global state, even if in a more elegant way.
With the advent of many Java framework the singleton pattern was made so popular that pretty much every developer now know it. For those that still have doubts, a singleton is an object that exists in a "single" instance along the whole application/framework. As an example, many GUI libraries provide a "Display" class, which is a singleton (the library needs a single display to draw onto); other examples could be a shared resource or service. The idea is simple: does the program require access to always the same object? If yes is the answer, chances are a singleton will be used.
How is implemented a singleton? Often it is implemented using a factory pattern like in the following snippet:public class Display{
// make constructor private
// so that nobody can build
// an instance autonomously
private Display(){ ... }
// a static reference to the
// ONLY available instance
private Display mySelf = new Display();
// use a factory method to get
// an instance of the Display
public final static Display getInstance(){
return mySelf;
}
}
The above example is kept simple just to show what and how a singleton is implemented; it is possible to make very complex and elegant implementations as well as to port the above code in any other programming language.
What is wrong with the above piece of code? That two patterns are involved: the singleton one and the factory one.
Now, singleton is bad, factory is good.
To explain the latter statement, allow me to introduce a piece of client code that is going to use the above Display class:
void showPopupWindow(){
Display.getInstance().showPopupDialog( "Hello World" );
}
What is strange with the above code? The method has no parameters, and is not accessing any private instance of the class. Let's see from another perspective: the client code knows where to find and how to get the Display instance, and therefore it is going to obtain it. This is a very strong coupling, and make code hard to read and test. Why? Let's start from the coupling first: the client code is going to access directly the Display class, and therefore there is no way to "inject" another Display instance to use. The two classes are therefore strictly coupling. This leads to the hard-test problem: how are you supposed to test the showPopupWindow() method if you cannot inject a mock-up Display object? The only way is to uninstall the real Display and use a fake one. It is not so simple. Finally, the hard-to-read problem: imagine you have access to the Javadoc documentation, that is something like:
/**
* Shows a popup window on the user display.
*/
void showPopupWindow();
You don't see what the code does in its implementation, so you have to guess that it is going to access the Display class to get an instance. Please note the use of the word "guess".
How is it possible to solve such problems? Let's start from the client code: adopt dependincy injection as follows
void showPopupWindow( Display display ){
display.showPopupDialog( "Hello World" );
}
or use an internal variable to keep an injected instance of Display and access it. Then refactor the Display code to use a separate factory:
public class Display(){
public Display(){ ... }
}
public class DisplayFactory{
private Display instance = new Display();
public static final Display getDisplayInstance(){
return instance;
}
}
Note that the instance is now cached into the factory, and that the Display class is pretty normal now. You can use some fancy protection mechanism, like making the Display's constructor package-visible and not public, but you get the idea. Now, no matter how you implement the Display class, the factory pattern is implemented the right way and the coupling among classes is strongly reduced. Moreover, you can also distribute the Display in binary form letting the clients to reimplement the factory pattern, while in the former case you could not. This problem lies in the fact that the first implementation of the Display class was playing a double role: the singleton and the factory for itself. This in particular is very common in many programs (and I did also!) but it is wrong. Note I'm not saying "it could be" wrong. A class must be thought as a unit with as less aims as possible, in particular with a single aim. So the right way to go is using a class as a singleton and one as a factory. This requires much more code and a few effort for initial setup but will pay in the long term.
1 commento:
Articolo molto utile.
Sono un programmatore ABAP e il tuo post mi ha chiarito le idee su un'implementazione a cui stavo lavorando. Grazie.
Posta un commento