Video version of the post:
This post covers:
- Singleton Pattern vs Global Variable
- Why people hate Singleton Pattern
- When to use Singleton Pattern
- Alternatives to Singleton Pattern
Singleton Pattern vs Global Variable
- They both provide a global point of access for an object or a variable
- The single pattern guarantees there’s only one instance, whereas Global variables don’t
- The single pattern also allows lazy instantiation, whereas Global variable don’t
Why people hate Singleton Pattern
With a quick search on the internet, we can see lots of articles written about why singletons are bad.
The first downside of the singleton pattern has to do with its global point of access to the instance.
This makes the Singleton Pattern very similar to global variables. And, at this point in time, the dust around global variable is settled: it's something ppl in the programming community despite with a passion.
So why is global access so bad? Because it makes the program none-deterministic and hard to reason about.
When someone reads this code, it’s very reasonable to assume the results of card_x.total_points and card_y.total_points are the same, since the code used to create and operates on card_x and card_y are identical. But since the instance method points to the same variable, the actual total_points depends on the order the code is called. If we create and add points to card x first, the result of total points from card x will be different than creating and adding points to card y first.
What’s even scarier is that we don’t know how many times add_points has been called before we create card x and card y. This makes it almost impossible to reason about the state of the cards without having to read through the whole code base and find out where and in what order add_points is called.
A global point of access also defeats the purpose of code design.
Code design in an essence is about setting up the proper encapsulations and boundaries among code to make programs easier to maintain and reason about.
Global variables defeat all boundaries because, well, since it's global, everyone can use and modify it which creates backdoors to all existing boundaries.
The book Refactoring To Patterns puts it elegantly: “The real problem with Singletons is that they give you such a good excuse not to think carefully about the appropriate visibility of an object. Finding the right balance of exposure and protection for an object is critical for maintaining flexibility.”
The other issue with the singleton pattern is that it encourages hidden dependencies.
So when should we use the singleton pattern?
First, take a close look to see if you actually need to use it.
The Singleton pattern is used to make sure there's only one instance of a class. In reality, it's very rare for us to run into this requirement.
Using the singleton pattern is more acceptable if the instance is only used to perform an action the remaining of the program doesn't depend on, like a void method without return values. Logging is a good example.
In the article Use Your Singletons Wisely, Rainsberger suggests three tests to see if you do need to use this pattern:
- Will every application use this class exactly the same way? (exactly is the key word)
- Will every application ever need only one instance of this class? (ever and one are the key words)
- Should the clients of this class be unaware of the application they are part of?
Alternatives to singleton pattern
We need to first understand that singleton Pattern breaks the Single Responsibility Principle, which says one class should only have one responsibility
In the case of singleton, the class not only holds the related business logic but is also responsible for ensuring the class has only one instance
"Why should the class itself be responsible for being a singleton? It seems quite logical for the application to take on this responsibility since the application requires this kind of behavior."
It's also recommended to put all of your singleton creation into one factory responsible for creating this kind of long-lived objects. By putting them in one place, the dangerous parts of the program are more obvious.