I read through many discussions related to Singleton Patterns, and different developers dislike it for different reasons. Not all the reasons make a pattern evil, because many of those reasons doesn’t really make your software really bad. I will be writing here each of the reason, and try to explain whether the reason is justified to call Singleton Evil:
1. Single responsibility violation
Singleton Pattern does at least three things:
- Object creation
- Application logic implementation
- Defines how to access the singleton object
Now as Singleton breaks the SOLID principle, it’s not a good practice and which cannot be called Evil. You can easily make a Factory class to eliminate the violation. So the factory and object class together becomes a “Super Singleton”. And you can indeed use the Factory the same way you have been using the Singleton. If Singleton is evil, so is “Super Singleton”. So getting rid of the violation doesn’t get rid of the EVIL; It lurks some where else.
2. Bad for unit testing
Singletons are bad for testing. Why are they bad? The reasoning I see is that you don’t know who is using the object and when. Okay, that problem goes for all kinds of instantiable classes, not only Singleton. You can indeed instantiate different objects in your application/business logic and make it hard to test. This assumed evil practice is pervasive and it cannot be attributed to Singletons, we have been doing it from a time before the birth of Singletons.
3. Global transitivity
When you have access to a global object, you actually have access to all the objects accessible by that global object. It’s true for all kinds of objects that your client have access to or can gain access to, not only singletons.
4. Global point of access
Anyone can get a reference to a Singleton by calling “getInstance”. That’s not purely evil either, you can get access to whatever declared public.
(this section is to be continued…)
The pure Evil
Let’s say you are a soldier at a military unit. And guns and bombs are objects provided by the unit management. If you could get guns at your will, you could do evil things and unit management would not be able to do anything about it. If you could craft a bomb yourself, you could do the same evil.
The evil is very simple by nature and true for both Singletons and Non-Singletons: (Creating &) accessing objects without permission from your application.
- So if you design a factory, and use the factory to get objects inside your logic, you are still creating a pathological liar.
- If you are creating objects of different classes just because they are public, you are feeding the evil.
- It’s your client ( who is making an app logic ) who is evil, not the Singleton pattern.
- It’s the pattern in which you have atrocious power to (create and) access a object.
The incorrect solution with Holder Pattern or Proxy Pattern
I saw many developers advice to use Proxy Pattern or Holder Pattern instead of singleton. But what does those patterns do? They only resolves the Single responsibility violation which is not the evil we have in our application. And more importantly they KEEP and FEED the same evil! You can use the holder or proxy to get the access anywhere you want.
The current & correct solution with DI and IoC
Guns or bombs, your management need to supply you with when they need a job done. You never get them, you never ask for them, you never craft them. Your sole purpose is to be used by the unit.
A habit: Dependency injection and IoC containers. Stay satisfied with what you are given, don’t steal or fabricate objects. Make singletons/factories to protect from hacks/exploits, but never use “getInstance()” outside containers. And this habit is for both Singletons and Non-Singletons. And I would still prefer a getInstance() or Factory.create() to ensure single object rule.
IoC Containers: google it. Two popular ones are Spring IoC and Guice.
Dependency Injection: google it
The future solution
DI, IoC, and ACL factories or Another layer of abstraction where a package cannot instantiate objects from another package except for application container. So, even you try to create or access directly, you are denied, some kind of Dependency Imposition or Control. I have several rudimentary thoughts on how to do that. I am willing to join a discussion with experienced developers or architects. Do invite me.