Singletonitis, Part 2

Published April 25, 2005
Advertisement
The Singleton Pattern's intent is to "ensure a class only has one instance, and provide a global point of access to it." [DP, p. 127]

It would seem that many people forget that the pattern embodies the whole definition there. Many "Singletons" are Singletons for no other reason than to take advantage of only a single property of the Singleton pattern. Using the examples listed previously, we'll continue from there.

Input Manager:
This is perhaps the biggest offender. The only reason it is a Singleton at all is because the programmer wanted to guarantee that only a single instance was created. Of course, this means that the programmer has conveniently tossed out half of the definition. The problem here, though, is one of visibility. In the tireless efforts of the programmer to prevent others from abusing the code, they have introduced an even worse exploit. The input manager is now globally visible. So how can the programmer guarantee that multiple instances of the object won't be created? Quite simply, you don't. Instead you handle the errors and throw the appropriate exception. Then in your documentation you CLEARLY indicate that only a single instance should be created.

Sound Manager:
This one is less of an offender than the input manager, but still pretty serious. The only reason it has been made a Singleton is to make it a global. Again, the programmer has tossed out half of the definition. First of all, we find that the sound manager doesn't actually require a global scope. The actual amount of code that uses it is fairly small. By applying refactoring techniques we can move these bits of code out to the appropriate subset of classes. Thus the visibility of the sound manager is reduced in scope from a global to a local. The real question is though: Why was this a Singleton in the first place, instead of a global? The simple answer is: The programmer wanted a global. However, he felt guilty about using an out and out global. So he took the next logical step, and wrapped it up in an object, thus he "OOified" it. The problem is, his solution isn't object oriented at all. In fact, it's very much not an object oriented solution.

Graphics Manager:
This one is the one true legitimate Singleton. The programmer wanted an object with only a single instance and a global point of access. However, just like the previous two, there is an issue of visibility. The graphics manager will be used in only a tiny subset of the overall code. While that code might be scattered all over the place, it is none the less, a small portion of the overall code (If it is otherwise, then you are probably writing a graphics engine, and not a game).

Avoiding Singletons:
So what is the real problem with Singletons? To quote Kent Beck: "The real problem with Singletons is that they give you such a good excuse not to think carefully about the appropriate visibility of an object." [Joshua, p. 116]

Remember, a Singleton is a global object. Once an object has been made into a Singleton, it's state can no longer be guaranteed, nor easily tested. Anyone can, and will, go in and change the state of the object.

So, how does one avoid Singletons? Simple, you apply refactoring [RF]. By refactoring your code you can do two things: The first is that you simplify the code. This makes it easier to maintain, extend, read, and reduces duplication. The second thing that you accomplish is that your code ends up being much more object oriented, and the visibility of objects becomes much clearer. Thus if you refactor your code then you can centralize the code that uses an object (that is a Singleton) and remove the need for it to have global visibility. Instead it goes from being a global to being a local or member variable. Where you can't centralize the code, pass the object via parameters, or parameter objects [Joshua].

Now, I am not saying that you shouldn't use the Singleton pattern. Instead I'm saying that you should very carefully consider the implications of making an object a Singleton. You should examine if the object really requires global visibility, or if you are just using it to get around a poor design. If it's the latter, then refactor the design to clean it up. In the words of Martin Fowler: "remember that any global data is always guilty until proven innocent." [PEAA, p. 482-483]

References
[DP] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design
Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley
Professional, 1995, ISBN 0-201-63361-2.

[RF] Martin Fowler, Refactoring: Improving the Design of Existing Code,
Addison-Wesley Professional, 2000, ISBN 0-201-48567-2

[PEAA] Martin Fowler, Patterns of Enterprise Application Architecture,
Addison-Wesley Professional, 2003, ISBN 0-321-12742-0

[Joshua] Joshua Kerievsky, Refactoring to Patterns, Addison-Wesley Professional,
2005, ISBN 0-321-21335-1.
0 likes 7 comments

Comments

Mushu
pfft.. you think Singletons are the result of bad refactoring? Take a look at this! Yeah, that's right. That was refactored once. Guess it's gonna take at least ∞ passes at this piece of crap code base before its actually workable...

Rawr. Making work for tommorrow today. [grin]
April 25, 2005 09:39 PM
stormrunner
I realize this may be ignorant, but I just have to ask - what would be a case for a singleton ? You have - in brutal fashion - destroyed the basis for almost all uses of the singleton pattern as it relates to game developement. The last bastion of defence, per se, lies in it's easy applicability for memory management or messaging schemes. But even that just seems like lazyness (i.e. Enginuity, signals and slots) ... so is the singleton applicable in any type of gamedev related task ?
April 25, 2005 10:41 PM
Ainokea
Ok I am going to refactor now.

Oh yeah, you not only make babies cry you make me cry too.
April 25, 2005 11:52 PM
_the_phantom_
@stormrunner
the only case for a singleton i can think of is a logging system, where it could be argued that you do need only one and global access to log from anywhere.

I now await Washu to shoot that arguement down [wink]
April 26, 2005 07:11 AM
Etherstar
What if I plan to make a singleton that will be used in other applications (albeit with one instance and global access)? Isn't the point of an OO language to provide encapsulation and code that is reuseable?

*ducks*
April 26, 2005 12:14 PM
Illco
Maybe I am missing some important point here and if so then please tell me, but in my opinion most of the arguments in this article are false.

Quote:
Argument: the singleton pattern makes the class globally visible which pollutes the global namespace.

Why must a singleton be in the global namespace whereas other objects do not have to be? A singleton is as globally available as any other type.

Quote:
Argument: the programmer cannot guarantee that multiple instances are created.

Not true. If the constructor is inaccessible and the GetInstance() function returns only the same instance, it is guaranteed that only one instance exists. The only glitch is thread safety which goes for all functions.

Quote:
Argument: global access is only waranted for classes that are used in a great part of code.

This means that whenever the size of code change, a smaller portion may be using the singleton. So when writing many lines of non-singleton code, suddenly the singleton is no longer warranted? What about future use of the singleton class in this or other projects?

Quote:
Argument: anyone can change the state of an object making it intractible.

This goes for any object. As long as the member functions are safe in this sense the state of the object is secured.

Again maybe I am totally besides some point here. And now its time to really duck for me too I guess. ;-)

Greetz,

Illco

PS: quotes are not literally but an extraction made by me
May 05, 2005 05:53 AM
Ahnfelt
Maybe I shouldn't reply to a thread that has been dead for two years, but here goes...

Quote:Original post by Illco
Why must a singleton be in the global namespace whereas other objects do not have to be? A singleton is as globally available as any other type.

The problem with singletons is not that they're global as such - it is that they have global state. This means that there will be a multitude of places in your code that access, and maybe even modify global variables. This is a maintenance nightmare, especially in the later case. If your singleton doesn't have (mutable) state, you're safe (because, well, it's not a singleton then).

Monostates are exactly as bad, since they provide the kind same access to global mutable state. Actually they are worse, since it's not obvious that they share state.

Classes on the other hand, do not have state. Only instances do, and those are not global (they have to be explicitly passed around as arguments).

Note that if you look at everything in your class that is declared "static" in isolation, you are looking at a singleton. If it has mutable state, you're probably in trouble.

My own rule is: don't make static variables, and don't make static constants that reference mutable objects. Note that this also excludes singletons. There really aren't that many exceptions.
July 30, 2008 11:40 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement