The article below describes the behavior of the Singleton design pattern as an anti-pattern, where it violates the basic principle of creating a single object.
We all are very much aware of Singletons, one of the main Creational Design Patterns. It's a very widely and commonly used Design Pattern. Most of you are aware of the advantages and the problems solved by the pattern, but I am not going to deal with those features in this article.
What most of us fail to understand is that in most of the cases, the Singleton is misused and acts as an anti-pattern. So let's try to look at the scenarios where the Singleton design pattern works as an anti-pattern.
Singletons are basically used as global variables. Using global variables is an enemy of encapsulation because it becomes difficult to define pre- and post-conditions for the client’s object interface. The working of the interface can be handled from within, and not from outside, the interface.
The use of Singletons makes it difficult to unit test classes because classes must be loosely coupled, allowing them to be tested individually.
When unit testing two classes, Singletons break the Single Responsibility Principle because one class is implementing Singleton and other is not. We need to pass in the Singleton as a parameter to the constructor, allowing the tester to mock out the Singleton class easily. The Singleton then does not has to enforce its own singularity. This can be done by factory classes eliminating the global state.
Singletons are bad when used with multi-threading because with just a single object, the options of threading are limited.
Singletons make it easy to break stuff. A file system is usually implemented as a singleton and, mostly, all the file operations are performed under a single method. However, binding the team to use just one common method to implement file operations makes it very difficult to implement.
Singletons decrease performance. If we have a number of lazily initialized Singletons, the compiler cannot fold multiple SingletonDemo.getInstance() calls into one. This is because each call to getInstance() will cause a branch instruction and possibly a cache mismatch.
Singletons promote tight coupling between classes. Singletons tightly couple the code to the exact object type and remove the scope of polymorphism.
Singletons do not complete what they are meant to because a number of techniques — namely Java Reflection, serialization, etc. — are available to create more than one instances of a Singleton class.
Using a static method to initialize Singleton objects is considered a good approach implementing Singletons. But remember, this approach forces the programmer to know the internal code structure of the class, as static methods can be invoked only on class names. Moreover, unit testing static methods is not an easy task using simple Mockito frameworks.
In the Agile world, we need to unit test even a single functionality. One of the most important things about these unit tests is that they must be independent of each other, making it difficult to implement with Singletons.
Extending Singletons is not easy. A programmer needs to use some kind of decorator pattern to change the behavior.
In a garbage collected system, Singletons can become very tricky in regard to memory management.
Singletons can’t be used with clustering.
The use of Singletons should be a very careful and calculated step because they can become a bottleneck during development and debugging.
Instead of using classes, emphasize using enums for implementing Singletons.