Sign in to follow this  

Why are singletons bad?

This topic is 3193 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Everywhere you look you will find intelligent people saying singletons are bad. For one they are hard to test (I don't understand why), the user (other programmers) don't know the dependancy of the methods using the singletons (I would think that is good, make the code as simple as possible). I am going through this in my head and I can't understand why for one, singletons are hard to test in your unit tests. Why would they be hard to test? Evenyone says they are, but doesn't give an example of why they are hard. The fact that they are persistant should not effect the design of your code just so you have pretty unit tests. If you need a new class that is a singleton, why not make a new unit test? And if its the classes the singleton contains you want to test, then why not call them directly? Are we redesigning our code for the convenance of easily making unit tests? Can someone shed some light on this for me? By the way I'm a C++ developer, so .NET, Java and the fact they have private classes don't matter to me. Thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMac
Are we redesigning our code for the convenance of easily making unit tests? Can someone shed some light on this for me?


Easily testable classes ~ easily usable classes.

Personally, I think Singletons are very useful for some things, for example configuration data, Resource Factories and so called "global objects" like the "application object".

Share this post


Link to post
Share on other sites
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?

[Edited by - Red Ant on March 17, 2009 3:40:16 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMac
That doesn't explain why singletons are hard to test, and why the development community hates them for some reason.
I haven't heard the "singletons are hard to test" argument, and it's certainly not the most important reason to avoid singletons. Singletons are essentially rebadged globals, and have exactly the same problems in an OO context as global variables. If you are an OO purist, globals and singletons both violate various pure OO principles. If you are not an OO purist, singletons are more complicated than globals, yet provide extremely little extra benefit.

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMac
That doesn't explain why singletons are hard to test, and why the development community hates them for some reason.


Hmmkay.

I don't know why singletons are hard to test, as I had never problems with that. oO

Many despise them because they are a pattern that's often used to make globals (aka spaghetti code) look nicer. Using singletons is often an excuse for lazy code designers, and it can create badly encapsulated/tightly coupled designs.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
The main thing I have against singletons is that, although they might make it easier to share "global" data, they also create dependencies upon the objects that use them. If you remove that singleton, the objects that depend on it will cease to function.

I do believe singletons have their place, but the trick is not to over-use them or create them out of laziness.

Share this post


Link to post
Share on other sites
Proper unit testing of any nontrivial system invariably requires mock objects and other types of injectable dependencies (ie, replacing real components while testing so you can isolate each system you want to test). This is difficult or impossible with the canonical singleton.

See also here.

And the book by Gerard Meszaros, which is superb even if you don't do strict TDD.

Share this post


Link to post
Share on other sites
Quote:
Original post by Konfusius

Personally, I think Singletons are very useful for some things, for example configuration data, Resource Factories and so called "global objects" like the "application object".


Personally, I find those objects prime candidates for non-singletons.

Configuration data allows me to pass specialized, modified or categorized configurations to subsystems. It also doesn't bind me to single configuration implementation.
Resource factories have either no state or immutable state, but frequently I find the need to configure them externally. I also frequently encounter situation where similar factories are used by different systems and need to be customized.
Application objects are something I use for the very reason to avoid global context, so that I can create multiple application within same process, and then run them in different threads, perhaps in a pool.

The way I prefer to bring all of that together is something like this:
class Application {
Application(Configuration & cfg)
: configuration(argc, argv)
, factory(cfg)
, subsystem(factory, cfg)
, anothersystem(cfg)
{}
};

...
int main(int argc, char **argv) {
Configuration cfg(argc, argv);
Application app(cfg);
app.run();
};
// or
int main(int argc, char **argv) {
Configuration cfg(argc, argv);
cfg["threads"] = 2;

Application app1(cfg);
// it *can* be a singleton too
// but doesn't need to be
Application app2(Configuration::get_default());
thread t1(app1, &Application::run());
thread t2(app2, &Application::run());
t2.join();
t1.join();
};





YMMV and one size does not fit all.

Quote:
Why would they be hard to test?


Ever tried unit testing a database application where you need to test DROP statement? In common design where database access is singleton or global hard-coded, accidentally running such test without changing the configuration will wipe the production system. So how do you test it automatically?

Much easier if systems are not coupled.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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().

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by jonathanjansson
Quote:
If you are an OO purist, globals and singletons both violate various pure OO principles.

I don't think they violate OO principles.

Well, they do. For more info, google Law of Demeter, Open/Closed Principle, and (arguably) the Acyclic Dependencies Principle.

Share this post


Link to post
Share on other sites
Quote:

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.

No, those are wants, and poor ones. The singleton pattern, as expressed by the GoF book (which is the de facto standard for definition of many design patterns) expresses the singleton as SimonForsman did. Your additions are just fluffy attempts at justification, and are non-canonical.

Share this post


Link to post
Share on other sites
Quote:
Original post by TheMac
I am going through this in my head and I can't understand why for one, singletons are hard to test in your unit tests.


You might be able to unit test a singleton, but you cannot unit test anything that uses that singleton.

A unit test by definition tests a single thing. If a class or function uses a singleton, then any test written for that entity will depend on the singleton and therefore the test is no longer a unit test.

If your singleton touches some larger system e.g. the windows registry, the file system, a database, etc, then your tests will also potentially have some nasty side effects. You don't want that stuff going on in your unit tests, or any other kind of test for that matter.

Share this post


Link to post
Share on other sites
On a few different occasions, I have tried to completely avoid using singletons. I kept ending up with more code, and a design that didn't work well. Sometimes an object is used in so many places, it just makes sense to have global access. I will say that trying to avoid singletons can lead to better code, but you have to give in sometimes.

Share this post


Link to post
Share on other sites
Quote:
Original post by incin
Sometimes an object is used in so many places, it just makes sense to have global access.


global access != you may only ever instantiate one;

Share this post


Link to post
Share on other sites

This topic is 3193 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this