Religious wars against particular design patterns just seem so, well, religious!
To me, a singleton is a particular implementation of a global variable. Nothing more. You use it one when you need one. It isn't magic or evil. It's a global variable.
The problem comes in when you are using one but don't know it. It's the beginner Spring problem. Developers don't realize that some libraries use them by default and start storing state there. Needless to say, not good.
Singleton pattern feels a bit like someone once heard that global variables were bad. Nonetheless people still need some sort of global state in some problems, but one couldn't use global variables because they were bad. So they invented alternative way to do exactly the same thing.
And not surprisingly singletons are being abused just like good 'ol global variables by bad programmers, and thus eventually they will be considered bad. And then a new way of handling some global state will be invented because singletons are bad and we still need some way of accessing some global state in some cases.
It's not quite exactly the same thing. It provides some control over initialization and (depending on the language) possibly eases synchronization, compared to a scattering of globals or a static class. It has the other problems of global variables, though.
Whereupon you can put tracing in to find out when the state was screwed up.
Singletons aren't bad, they're just misused a lot by bad programmers. It's like saying "Food is bad," and sure, people misuse food and hurt themselves, but that doesn't mean we should ban food.
It's more like saying "poison is bad", and losing the benefits of bleaching the bathtub, just because people keep killing their kids with it by accident.
Except it's nothing like saying "food is bad". If you don't ever eat food, you die. If you don't ever use singletons, some of your code has an additional parameter uglily threaded through it.
Tracing can be very lightweight. Not utterly non-instrusive, but pretty close. As a kernel / games dev, I'm quite aware of timing issues.
You get into serious trouble with "lock free" stuff, where pretty much any additional activity (even register operations) will perturb things and make bugs go away. Hopefully at this point you have help from the hardware. Then again, if you're doing LF, you're in a special place and probably have the chops to deal with it...
That is a feature of unconstrained, shared, mutable state. While singletons are often used that way, there are two problems with the resulting code. On the other hand, imagine a hypothetical situation where you have a bunch of static data that you would prefer is only generated once, and only ever generated if needed. A singleton gets this done, without any sort of "where on earth is this set to X?" but retains issues with injection for testing and the like (depending on precisely how it's implemented, in most languages).
> To me, a singleton is a particular implementation of a global variable.
Except that it's not. Global variables aren't initialised lazily, and their type can be an interface just fine. I can think of a few valid use cases for singletons, but it's not a great replacement for global variables IMO. Service locators are closer, and I think they have far more valid use cases than singletons.
> Except that it's not. Global variables aren't initialised lazily
Depends on the language, and the global.
In java, a static attribute is initialised when the class is first used. Until the class itself is accessed and used, the attribute remains uninitialized.
In clojure, delay[0] will invoke (and cache) its body the first time it's deref'd.
A getInstance() call can return a pre(statically)-initialized instance if it wants to. Laziness is optional. The pattern gives you the freedom to do it either way.
To me, a singleton is a particular implementation of a global variable. Nothing more. You use it one when you need one. It isn't magic or evil. It's a global variable.
The problem comes in when you are using one but don't know it. It's the beginner Spring problem. Developers don't realize that some libraries use them by default and start storing state there. Needless to say, not good.