Search this blog

08 September, 2009

You should already know...

...that singletons are a bad, baaad, bad, idea: The Clean Code Talks - "Global State and Singletons"

It's interesting how people think that globals are bad, but singletons are not. They are the same thing, but still, internet noise can really destroy your brain.

You're told that globals are evil, and you're told that design patters are good. There is so much noise about that, they just become facts, with no need of reasoning around those.

Singletons are a design pattern (probably the only one you really know or seen actually used), so they're good.

Now try to remove that brainwashed ideas from your programmers minds... They won't believe you so easily, everyone loves design patterns (in my opinion, they're mostly crap), but you can use internet at your advantage. The video I've posted is on youtube, and done by google. That should be a very powerful weapon. Enjoy.

8 comments:

Nick said...

Far be it for me to argue with the great google in the sky, but...

I think the real problem is that Java, C++, and so on have a missing feature, which is the ability to specify order of static instantiation. The second problem, at least for Java, is ambiguity about whether global means global to an app, or global to an instantiated vm. For C/C++ the ambiguity would be vs. weak linkages in multiple shared libraries.

I have to agree about the Symptom, that global/singleton is bad in the general case, but have to say that they are not the Cause! The proposed solution, which I guess is weak functional programming (dependency injection), is valid, but leads to either passing a lot of extra stuff around the stack, or a lot of extra local pointers in objects that retain a pointer (presumably shared) to the One True Instance. So I can't say I like the proposed solution, but can see that it is Correct.

Other alternatives include instantiating your globals manually at startup, and not allowing threads to start until your dust has settled. This has the downside of requiring much diligence, and knowledge of exactly what is going on, very difficult to achieve when you have many programmers on a project and deadlines.

So in the end, I guess I have convinced myself, and I am NOT arguing against the Google talk, but am sure that at least Java and C++ lack important features necessary for correct operation in general cases.

I have to mention, I've never shipped a game that didn't have global variables :)

Cubee said...

Singletons are not per se bad.
If they are immutable, they are pretty good in fact.
E.g. an instance of an empty string as a singleton.

This could be considered as a form of very trivial object pooling.

DEADC0DE said...

Nick: the inability of controlling the order of initialization of globals is not a problem. Singletons in fact solve that, and you can also code versions of them that explicitly need to be initialized and destroyed right? So if that was the problem, we already had the solution...

Singletons are bad because they cause hidden dependencies between modules, they make modules talk to each other even if they were not supposed to (see http://c0de517e.blogspot.com/2008/04/singletons-new-superglue.html). And dependencies are evil (http://c0de517e.blogspot.com/2009/09/code-quality.html).

P.S. I dunno what you mean with "weak functional programming", passing references around is far from being functional

Cubee: yep, you're right, constants are good. Even if a constant is seen by the entire application, thus causing a dependency, it's a dependency only to the constant, and it doesn't glue together modules, it doesn't spread. So it's perfectly fine.

Nick said...

> Singletons are bad because they cause hidden dependencies between modules

Yes, I think we are arguing the same point. Sorry for the rambling non-argument as it doesn't really make my point well, which was intended to be about static initialization before main runs.

I went and read your earlier posts on singletons, and the linked "Performant Singletons" article. I learned that I am not a code patterns person at all :) I had understood singleton to mean a single instantiation of an object. I wouldn't expect to code the object such that it knows and enforces its own singleton-ness. I see however that the code pattern suggests that the singleton knows and enforces its own singleton-ness. Looking at some Singleton templates, (http://www.codeproject.com/KB/cpp/singleton_template.aspx is pretty typical) I can say I don't like Singleton one bit, and have been misusing the term. In fact, I am inspired to correct all instances of the word singleton in my code.

My comment about functional programming was only that in functional programming state and mutable data have no place, so passing managers around to functions reminded me of that. I re-read slide 56 of the presentation and see that they aren't passing objects around, but registering pointers to objects in other objects. That basically proves I shouldn't write long comments withougt enough sleep.

Anyway, I have certainly learned from this thread, so thanks for the post.

DEADC0DE said...

Nick: Yep, having a single instance, without enforcing it is fine. You just have a normal class, and call new once, and that's it.

The problem with the singleton pattern is that by enforcing it to be a singleton, it makes it global and globally accessible.

On an unrelated note I don't really understand how people can even make the design decision of declaring a class a singleton. How can you be really, really sure that you will ever, ever need just one of such classes? The decision is not made based on that reasoning, all the times, is made because you want that class to be global. So the pattern should be called just "global". And then it would expose to the world its evilness and its uselessness :)

Oh and I know I was being picky about my remark on the functional comment. I actually understood what you meant, I wanted sort of let you think that the fact you're passing something around, does not make anything more or less functional, if what you're passing is not an immutable copy at least :)

Ben Voigt said...

"by enforcing it to be a singleton, it makes it global and globally accessible"

Say what?

class IsSingleton
{
static int count;
public:
IsSingleton() { if (interlocked_exchange(&count, 1) != 0) throw "singleton violation"; }
IsSingleton(const IsSingleton&) { throw "singleton cannot be copied"; }
// rule of three exception: operator= can't be invoked except reflexively, because only one IsSingleton instance can ever exist.
~IsSingleton() { count = 0; }

// actual useful stuff here
};
int IsSingleton::count = 0;

DEADC0DE said...

Ben: Sorry, I was talking about the singleton pattern. I won't have anything against designing a single-instance class as you did. Even if I still don't understand where it could be useful. Maybe it could be useful once in your coding life, and I would be fine with that. But it won't become a pattern... The singleton pattern is so used not because it enforces the singleton-ness, that is something rarely useful, but because it makes things global (with a nice makeup), that people find handy even if it's crappy design.

Ben Voigt said...

@DEADC0DE: I guess you're talking about using a private constructor to prevent any instances save for one static member, and a public accessor? But that doesn't support polymorphism or the inversion of control pattern (there can only be one instance derived from this particular class, but the exact type of that instance and constructor parameters are controlled via configuration). The singleton-with-IoC pattern then can't use a private constructor, it has a public or protected constructor that enforces the singleness. Typically that could be done via interlocked_compare_exchange of the this pointer into the static member pointer to the one true instance, and the public accessor returns that static pointer.