Why are singletons bad?

Started by
90 comments, last by _moagstar_ 15 years, 1 month ago
A possible explanation: let's suppose a singleton allocates a resource. You call code that might in turn call that singleton, or might not. Now comes the end of your program: will you have to destroy the singleton ? do you even know which singletons were instantiated ?

Of course the problem could be only with singletons allocating resources.
Advertisement
I think the biggest problem with Singletons and globals is the problem of lifetime.
Globals are awsome!(tm) when you think about the fact that you can just store a global, drop it to the debugger and tweak it without having to even set a breakpoint (let alone set a breakpoint and change the value every time it hits it). (yeah yeah, quake console or the like is the better solution)
Globals mean you don't have to pass things around as parameters.

BUT, globals lack the wonderful lifetime rules of everything else. So using them to store anything that needs order of initialization, or contains state is a BadThing(tm). It becomes a pain in the ass to test something when the time of creation can change (ie you don't explicity require a "construct" in your singleton but allow "getInstance" to run init code if there is nothing to get yet).
They are bad for threading (one instance means you can't buffer it the same as everything else, meaning it probably needs a mutex)
The support bad design, as they take everything to the top scope. It is much better to design an OO system with each layer tucked in as tight as it can go. It keeps your objects from getting modified in places where you don't want or expect (especially when working with a team of people). Thus making you thing out better designs for how any two objects are supposed to interact.
Quote:Original post by Red Ant
I guess the thing that's bad about about singletons is they let you get away with sloppy design because you don't really have to think much about why in the world Module A should be passed a reference to Module B. You just call A::GetInstance().doSomething() and are done with it. Whereas if you'd have to pass a reference to get the job done maybe you'd stop to think, hmmm ... I'm passing references to Module A to all kinds of things in my program, even some of the modules that shouldn't really need it ... maybe I should rethink my design?


Your code should be as easy to use as possible, keep it simple. In large projects, its not a good use of time to know everything thats going on, only the pieces you need.
Quote:Original post by TheMac
Your code should be as easy to use as possible, keep it simple. In large projects, its not a good use of time to know everything thats going on, only the pieces you need.



How does that contradict what I said?
I guess Singletons could be useful if you have an abstract base class (eg GraphicsDevice) and a concrete implementation (eg D3D9GraphicsDevice) that can only exist once.

Or so you can use MyClass::Instance everywhere and pretend it's not a global and that its order of initialization is better/easier than calling InitMyGlobalState().
Anthony Umfer
If you google "singleton bad", you will get videos, blogs, and articles with developers saying how bad singletons are, and for some reason global scope variables gets thrown in the mix as well. Maybe the current fab is to not like singletons because developers can miss use the pattern, but any patteren can be misused. I just feel there is a witch hunt going on when if you use patterns properly, we wouldn't have to worry about it. I still do see why unit testing is so difficult, but then again maybe I need to consider a more complex example.

Thanks for everyones input.
Quote:Original post by nsto119
People hate them because they're essentially global variables, and people have drilled into their head from the day they start programming that globals are bad.


Its not the globals that are the problem, The problem is global state (a singleton that doesn't hold a state could just as well be implemented as a collection of functions and constants in a namespace)

If you want there to be a single global instance of a class you should not use a singleton, you should create a single global instance of the class.

A singleton is the right choice only when things will break if you create more than one instance of the class and you need global access to it.
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Quote:
A singleton is the right choice only when things will break if you create more than one instance of the class and you need global access to it.


Or there is no need for another of the same object, and the code is easier to use. Or singletons could be used to contain boiler plate code that the developers would have to run every time they want to do something.
To determine what a bit of code does, you need to understand it's environment and it's effects.

If the code is functional (in the programming sense, not the idiomatic English sense -- ie, no side effects), the effects of the code can be bounded.

If the code doesn't access global state, then the environment of the procedure is exactly the arguments passed to the procedure.

A bit of code that is both functional and lacks access to global state is known as a 'pure function'.

Pure functions can be tested very easily. You can build test harnesses that run them in an arbitrary environment, and you just have to check their output to see if they worked right.

A function that has write access to global state, however, means that you have to check your entire global state to make sure that it hasn't screwed up. You can understand how this is harder than checking the return value.

A function that has read access to global state is also a bitch. You now have to either figure out exactly what parts of the global state parameter space it reads from in order to find the space of test environments, then expose it to the entire range -- or you have to give up on examining the input space of your function, and hope all goes well.

Singletons and Global variables become input state to every function in your entire application. If they can be written to by anyone, they are also part of the output state. Now your Global variables and Singletons changes need to be regression tested against every component of your entire application.

Or you can just throw your hands up and say 'I hope it works'. After all, in simple programs, it usually does...

By restricting your input state and your output state, it is easier to test a component. (Note that you can still have hard components to test that have a very restricted input/output space -- ie, a go game AI that takes a go board, and returns the best next move. Ridiculously hard to test. Easy to describe input/output space.)

Singletons in particular are classes that _prevent_ multiple instances of themselves from being created. As noted above, if you have a database singleton, you cannot build a subprocedure that operates on a clone of the database instead of the actual database in your program. Because your database type is a singleton.
Quote:If you are an OO purist, globals and singletons both violate various pure OO principles.


I don't think they violate OO principles.

This topic is closed to new replies.

Advertisement