Why are singletons bad?

Started by
90 comments, last by _moagstar_ 15 years, 1 month ago
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.
Advertisement
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".
That doesn't explain why singletons are hard to test, and why the development community hates them for some reason.
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]
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.
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.
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.
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.
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.
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();};// orint 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.

This topic is closed to new replies.

Advertisement