Design patterns are very useful. They help us create readable and maintainable code. But there’s only one pattern that has gone on to garner infamy. Some people might call using it a moral failure: The evil Singleton.
So how much damage must one pattern do to win that title? Since I’m an avid singleton user, I thought I’d chime in.
Here’s an example. This is a factory pattern, which we usually Implement in order to create one.
public class Factory {
private static VeryExpensiveSingleton theInstance;
public static VeryExpensiveSingleton getInstance() {
if (theInstance == null) {
theInstance = new VeryExpensiveSingleton();
}
return theInstance;
}
}
Code language: PHP (php)
It is created once, and everybody who wants to use it, will access it through our factory, and get the same instance.
One Singleton To Rule Them All
As with every villain, our Singleton has a very interesting origin story. It was really good and helpful at the beginning.
The idea behind the Singleton pattern, is that if we have a resource that is very expensive to create or maintain, we probably wouldn’t give a separate instance of it to everyone. Otherwise we would clog up the system resources, or the system might crash. Thus the Singleton pattern was born: One single instance to be used by everyone.
Singletons don’t have to be objects in memory, like in our code. Sometimes, we give a database connector as an example. But, the database itself is a shared resource. It is expensive – to buy, to install, to set up and to clean up. And it services multiple clients. Having one “instance” of it makes sense.
We can do the same with handles, pointers, global state. Singletons are there to serve society, so why all the hate?
Singletons in Tests
Welcome to my world: The world of testing. With tests, we need isolation. We need every test to run in isolation from all the other tests. Singletons break this premise. So in fact, each test requires its own little instance of the singleton, which is, of course, not a singlton.
Could be a multington. Not sure.
The purpose of the singleton is to be irreplaceable, to be unique. Remember our factory? The VeryExpensiveSingleton can be obtained only through the getInstance method. It is created only once. It is the Holy One. A singleton does not become single, until we put a lot of barriers around it to make sure it is a Singleton.
So back to testing. We want a different instance that we can control, for every test. Let me read your mind. You’re thinking about mocks, right? Separate mocks can simulate the Holy One, separately.
So how do we create a mock and use it in our tests?
If we use our factory as-is, it’s not that simple. We can change the code, though. How about we this very minor change?
public class Factory {
public static VeryExpensiveSingleton theInstance;
public static VeryExpensiveSingleton getInstance() {
if (theInstance == null) {
theInstance = new VeryExpensiveSingleton();
}
return theInstance;
}
}
Code language: PHP (php)
I made the instance public, now everybody, especially our tests, can modify it, right?
What’s that?
I can’t hear what you’re saying. Maybe it’s because all the screaming of “NOOOOOO! It’s not a singleton anymore!”
Oh, but I assure you, it still is. In production there will be only one, I promise. Really, I checked the code.
What? You still don’t believe me?
Or maybe, you’re afraid that someone, somewhere in the future, let’s call them an idiot, might replace that singleton in production?
After all, making the instance private was supposed to keep the idiots away, right? Well, unfortunately no. The problem with idiots, is that they have both motivation and skill. They will find a way.
So let’s get back to our singleton. Is it really evil?
A singleton is not really evil. All the evil-ness that we’re talking about comes, from the need to change the code for the tests. Breaking our Immaculate Design.
Let’s talk about this Immaculate Design, that doesn’t support testing. Maybe the design is good (for some purposes), but without the ability to test it, I don’t have proof that the code actually works. We have two options: Either change the code, or drop the tests.
Tests are tools that we use to ensure that the code we deliver, works. Unfortunately, In the real world, we don’t get points for good design. We get points for code that works.
So, if we need to change the code, it’s not for the sake of the tests. It is to make sure that the code that we deliver, actually works. Before the change, it was untestable. Now it is different, but comes with proof that it’s working.
Singletons are not evil, and patterns are not holy. If my code is not testable, you better be sure that I’m going to make it testable, sometimes by changing it, or writing it like that in the first place.
And I’ll have that proof.
Code is there for the changing. If you want better code, invite me to do “Work On Your Code” sessions. You’ll learn how to properly use patterns, and how to make your code testable.
2 Comments
David V. Corbin · October 22, 2024 at 4:59 pm
I put forth that is an instance of an items is “Expensive” and there must be “only one” that it should most certainly be an interface and that all items that rely on it should take that interface.. Now we can use different implementations when we need to pass something for testins….
Similar to avoiding “calls to new” inside of classes [use a factory, or injection or even a taco], the same can be applied to accessing the singleton provider….
[ps: yeah, it took some getting used to, but a decade or ore of using the above has proven quite effective. YMMV]
Gil Zilberfeld · October 24, 2024 at 11:55 am
A pattern is just a pattern. There are many techniques to achieve what we want, in this case, have single way to access to an object. Also, maybe have just one object, doesn’t solve the problem, or, maybe it solves it but creates other problems. There’s no one way to achieve what we want. None of those are evil.