views:

1794

answers:

7

I've been reading about Singleton pattern for last few days. The general perception is that the scenarios where it is required are quite few (if not rare) probably because it has its own set of problems such as

  • In a garbage collection environment it can be an issue with regards to memory management.
  • In a multithreaded environment it can cause bottlenecks and introduce synchronization problems.
  • Headache from testing prespective.

I'm starting to get the ideas behind these issues but not totally sure about these concerns. Like in case of garbage collection issue, usage of static in singleton implementation (which is inherent to the pattern), is that the concern? Since it would mean that the static instance will last till the application. Is it something that degrades memory management (it just means that the memory allocated to the singleton pattern won't be freed)?

Ofcourse in a multithreaded setup, having all the threads being in contention for the singleton instance would be a bottleneck. But how does usage of this pattern causes synchronization problems (surely we can use a mutex or something like that to synchronize access).

From a (unit?)testing perspective, since singletons use static methods (which are difficult to be mocked or stubbed) they can cause problems. Not sure about this. Can someone please elaborate on this testing concern?

Thanks.

+18  A: 

If you haven't seen the article Singletons are Pathological Liars, you should read that too. It discusses how the interconnections between singletons are hidden from the interface, so the way you need to construct software is also hidden from the interface.

There are links to a couple of other articles on singletons by the same author.

Greg Hewgill
+11  A: 

When evaluating the Singleton pattern, you have to ask "What's the alternative? Would the same problems happen if I didn't use the Singleton pattern?"

Most systems have some need for Big Global Objects. These are items which are large and expensive (eg Database Connection Managers), or hold pervasive state information (for example, locking information).

The alternative to a Singleton is to have this Big Global Object created on startup, and passed as a parameter to all of the classes or methods that need access to this object.

Would the same problems happen in the non-singleton case? Let's examine them one by one:

  • Memory Management: The Big Global Object would exist when the application was started, and the object will exist until shutdown. As there is only one object, it will take up exactly the same amount of memory as the singleton case. Memory usage is not an issue. (@MadKeithV: Order of destruction at shutdown is a different issue).

  • Multithreading and bottlenecks: All of the threads would need to access the same object, whether they were passed this object as a parameter or whether they called MyBigGlobalObject.GetInstance(). So Singleton or not, you would still have the same synchronisation issues, (which fortunately have standard solutions). This isn't an issue either.

  • Unit testing: If you aren't using the Singleton pattern, then you can create the Big Global Object at the start of each test, and the garbage collector will take it away when the test completes. Each test will start with a new, clean environment that's unnaffected by the previous test. Alternatively, in the Singleton case, the one object lives through ALL of the tests, and can easily become "contaminated". So yes, the Singleton pattern really bites when it comes to unit testing.

My preference: because of the unit testing issue alone, I tend to avoid the Singleton pattern. If it's one of the few environments where I don't have unit testing (for example, the user interface layer) then I might use Singletons, otherwise I avoid them.

Andrew Shepherd
The comment about memory management is wrong in C++, in my opinion. The "singleton" as a member object of the application object (as defined by the framework), for example, has a *well-defined* time of destruction. Any singleton implementation based on a "static" somewhere is prone to the static initialization / destruction order fiasco. I've seen some really ugly hacks to get around accesses to singletons from the destructors of other singletons.
MadKeithV
This is pretty much what I say in these two blog posts: "Uncontrolled coupling" - http://www.lenholgate.com/archives/000259.html and "Singletons and testing" - http://www.lenholgate.com/archives/000357.html. Use the "good half" of the singleton design pattern (only one instance) without the bad half (accessible from everywhere) and many of the singleton problems go away. Once you get to here you can pretty much move away from the "good half" as well as much of the time things are singletons just because of the "accessible from everywhere" thing, i.e. they are really just global objects...
Len Holgate
Why is "only one instance" the "good half"? Why would you ever want that? That seems to me to be an even worse problem than global accessibility. Globals are *sometimes* useful. But by preventing creation of more than one instance, you're crippling your code base. You don't know how the class is going to be used in the future, and yet you happily nail it down and prevent all but the most trivial usages of it.
jalf
Your analysis of the problem with multithreading bottlenecks is flawed. The MyBigGlobalObject.GetInstance() method will need to be thread safe (to prevent more multiple threads creating multiple singleton instances). Thus, you will have synchronisation issues with the singleton version even when the MyBigGlobalObject methods are inherently thread safe and don't require any locking overhead. If you just create a normal global object and pass it around, you can create it in the original thread, before you create additional threads, and avoid this overhead.
Stephen C. Steel
+15  A: 

In a garbage collection environment it can be an issue with regards to memory management

In typical singleton implementations, once you create the singleton you can never destroy it. This non-destructive nature is sometimes acceptable with the singleton is small. However, if the singleton is massive, then you unnecessarily using more memory than you should.

This is a bigger issue in languages where you have a garbage collector (like Java, Python, etc) because the garbage collector will always believe that the singleton is necessary. In C++, you can cheat by delete-ing the pointer. However, this opens its own can of worms because it's supposed to be a singleton, but by deleting it, you are making it possible to create a second one.

In most cases, this over-use of memory does not degrade memory performance, but it can be considered the same as a memory leak. With a large singleton, you are wasting memory on your user's computer or device. (You can run into memory fragmentation if you allocate a huge singleton, but this is usually a non-concern).

In a multithreaded environment it can cause bottlenecks and introduce synchronization problems.

If every thread is accessing the same object and you are using a mutex, each thread must wait until another has unlocked the singleton. And if the threads depend greatly upon the singleton, then you will degrade performance to a single-thread environment because a thread spends most of its life waiting.

However, if your application domain allows it, you can create one object for each thread -- this way the thread does not spend time waiting and instead does the work.

Headache from testing prespective.

Notably, a singleton's constructor can only be tested once. You have to create an entirely new test suite in order to test the constructor again. This is fine if your constructor doesn't take any parameters, but once you accept a paremeter you can no longer effective unit teest.

Further, you can't stub out the singleton as effectively and your use of mock objects becomes difficult to use (there are ways around this, but it's more trouble than it's worth). Keep reading for more on this...

(And it leads to a bad design, too!)

Singletons are also a sign of a poor design. Some programmers want to make their database class a singleton. "Our application will never use two databases," they typically think. But, there will come a time when it may make sense to use two databases, or unit testing you will want to use two different SQLite databases. If you used a singleton, you will have to make some serious changes to your application. But if you used regular objects from the start, you can take advantage of OOP to get your task done efficiently and on-time.

Most cases of singleton's are the result of the programmer being lazy. They do not wish to pass around an object (eg, database object) to a bunch of methods, so they create a singleton that each method uses as an implicit parameter. But, this approach bites for the reasons above.

Try to never use a singleton, if you can. Although they may seem like a good approach from the start, it usually always leads to poor design and hard to maintain code down the line.

carl
+2  A: 

My main argument against singletons is basically that they combine two bad properties.

The things you mention can be a problem, sure, but they don't have to be. The synchronization thing can be fixed, it only becomes a bottleneck if many threads frequently access the singleton, and so on. Those issues are annoying, but not deal-breakers.

The much more fundamental problem with singletons is that what they're trying to do is fundamentally bad.

A singleton, as defined by the GoF, has two properties:

  • It is globally accessible, and
  • It prevents the class from ever being instantiated more than once.

The first one should be simple. Globals are, generally speaking, bad. If you don't want a global, then you don't want a singleton either.

The second issue is less obvious, but fundamentally, it attempts to solve a nonexistent problem.

When was the last time you accidentally instantiated a class, where you instead intended to reuse an existing instance?

When was the last time you accidentally typed "std::ostream() << "hello world << std::endl", when you meant "std::cout << "hello world << std::endl"?

It just doesn't happen. So we don't need to prevent this in the first place.

But more importantly, the gut feeling that "only one instance must exist" is almost always wrong. What we usually mean is "I can currently only see a use for one instance".

but "I can only see a use for one instance" is not the same as "the application will come crashing down if anyone dares to create two instances".

In the latter case, a singleton might be justified. but in the former, it's really a premature design choice.

Usually, we do end up wanting more than one instance.

You often end up needing more than one logger. There's the log you write clean, structured messages to, for the client to monitor, and there's the one you dump debug data to for your own use.

It's also easy to imagine that you might end up using more than one database.

Or program settings. Sure, only one set of settings can be active at a time. But while they're active, the user might enter the "options" dialog and configure a second set of settings. He hasn't applied them yet, but once he hits 'ok', they have to be swapped in and replace the currently active set. And that means that until he's hit 'ok', two sets of options actually exist.

And more generally, unit testing:

One of the fundamental rules of unit tests is that they should be run in isolation. Each test should set up the environment from scratch, run the test, and tear everything down. Which means that each test will be wanting to create a new singleton object, run the test against it, and close it.

Which obviously isn't possible, because a singleton is created once, and only once. It can't be deleted. New instances can't be created.

So ultimately, the problem with singletons isn't technicalities like "it's hard to get thread safety correct", but a much more fundamental "they don't actually contribute anything positive to your code. They add two traits, each of them negative, to your codebase. Who would ever want that?"

jalf
I think you're creating a straw-man argument about the "accidental" creation aspect. I see one benifit of the singleton being that a new developer can come on board, see the code, and know with ABSOLUTE CERTAINTY that there is only one instance created, accidental or otherwise. That's not to detract from your other points.
Andrew Shepherd
And what difference does that make, in the big picture? If the new developer can't come on board and trust what his coworkers say about the code (such as, "only one instance is created", then he's going to have to manually doublecheck everything else as well. So no, I think you're making the straw-man argument. The newcomer is going to have to trust his coworkers word on pretty much anything else, so I really don't see why he need ABSOLUTE CERTAINTY about *this* particular issue.
jalf
I've always thought the accidental instantiation argument (favoring singletons) was a bit fallacious. But it's the first time I read someone else with the same idea :-)
Vinko Vrsalovic
I' only add that there is sometimes a case around access to certain hardware that "doesn't play well with others" (I'm thinking of a certain company's telsets right now) /mutter mutter/
Basiclife
+1  A: 

About this unit testing concern. The main problems seems to be not with testing the singletons themselves, but with testing the objects that use them.

Such objects cannot be isolated for testing, since they have dependencies on singletons which are both hidden and hard to remove. It gets even worse if the singleton represents an interface to an external system (DB connection, payment processor, ICBM firing unit). Testing such an object might unexpectedly write into DB, send some money who knows where or even fire some intercontinental missiles.

Rafał Dowgird
A: 

I agree with the earlier sentiment that frequently they're used so that you don't have to pass an argument all over the place. I do it. The typical example is your system logging object. I typically would make that a singleton so I don't have to pass it all over the system.

Survey - In the example of the logging object, how many of you (show of hands) would add an extra arg to any routine that might need to log something -vs- use a singleton?

RobertL
You don't need to pass it to each routine, you can simply pass a reference to it in the ctor or via a setLogger(Logger" as a member data, the second would require you to use "Logger* logger" and add checks before use - that could be hidden in a private operation.
YermoungDer
I cheat - My loggers implement ILogger and I have a meta-logger which ALSO implements ILogger and just wraps calling multiple ILogger methods, then I use dependency injection to give the right logger to my classes. I never have to worry about it again - in dev, I can log to console, in prouction to DB an event log or switch on verbose and get complete memory dumps to file :)
Basiclife
A: 

I wouldn't necessarily equate Singletons with Globals. Nothing should stop a developer from passing an instance of the object, singleton or otherwise, as a parameter, rather than conjure it out of the air. The intent of hiding its global accessibility could even be done by hiding its getInstance function to a few select friends.

As far as the unit testing flaw, Unit means small, so re-invoking the app to test the singleton a different way seems reasonable, unless I'm missing something the point.

no-op