Singleton pattern abuse

Started by
44 comments, last by freakchild 11 years, 10 months ago

Just a thought... whether you're using a pass-by-argument sort of thing, or a singleton, you're still using that object in the same places.
[/quote]
In my experience, once an object becomes globally accessible, the probability of "unnecessary" accesses increases enormously. So, no, I think in practise one finds that your average global will be accessed by considerably more areas of the code than the same state would be accessed in a less coupled design.

Also, once there is at least one globally accessible object, the probability of other (arguably far less worthy - it is easy to argue for a global texture cache) candidates being "promoted" to globally accessible increases too.
Advertisement


Just a thought... whether you're using a pass-by-argument sort of thing, or a singleton, you're still using that object in the same places.

In my experience, once an object becomes globally accessible, the probability of "unnecessary" accesses increases enormously. So, no, I think in practise one finds that your average global will be accessed by considerably more areas of the code than the same state would be accessed in a less coupled design.

Also, once there is at least one globally accessible object, the probability of other (arguably far less worthy - it is easy to argue for a global texture cache) candidates being "promoted" to globally accessible increases too.
[/quote]

True enough, it is important to practice some self control to avoid overusing them. But just because there's a risk of over-/misuse is no reason to completely discard something.

I refer you to this lovely piece I wrote some time ago. I'm actually quite proud of it (especially the second version, near the bottom of the page). In it, just about everything I've used has been misused. But it's no reason to condemn any of them (can't say for loops are bad just because it's possible to put the entire loop body inside the conditional and/or increment sections); only those particular cases. But, that's getting a bit off-topic I think.
<br />I refer you to this lovely piece I wrote some time ago.<br />


I don't understand your point here. That code is over-complicated, difficult to understand and hard to maintain and can be implemented far more simply at no significant cost. Many of the arguments against singletons would apply equally to that code.

There are many reasons to condemn the majority of your code I'm afraid. "It works" is about the only thing that can be claimed in its favour, which is subject to testing to confirm.

I refer you to this lovely piece I wrote some time ago. I'm actually quite proud of it (especially the second version, near the bottom of the page). In it, just about everything I've used has been misused.


That's a nice one! I like the way you use the commutative property of the indexing operator..

I once created an encryption program. It used C as input, and produced compilable C as output. If you change all identifiers and expand most local header files, the source code can become really hard to understand.
[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

[quote name='Sir Timothy' timestamp='1339354691' post='4947993']<br />I refer you to this lovely piece I wrote some time ago.<br />


I don't understand your point here. That code is over-complicated, difficult to understand and hard to maintain and can be implemented far more simply at no significant cost. Many of the arguments against singletons would apply equally to that code.

There are many reasons to condemn the majority of your code I'm afraid. "It works" is about the only thing that can be claimed in its favour, which is subject to testing to confirm.
[/quote]
I think the key comes from the next sentence: "In it, just about everything I've used has been misused." I think the code is more satyrical in nature, showing you all the wrong ways to use otherwise useful aspects of C. Which would make sense in the context of his earlier post, where he's neither really for nor against singletons, but mentions it's easy to abuse them (i.e. I think his point his: do you condemn the pattern or do you condemn the use of the pattern?).

I won't repeat my personal opinions (which I expressed here), as this topic was beat to death on these forums within the last 3 months. I will say, however, that I have never seen the singleton used properly. Ever. It's always used as a "OOP global variable" (I mean, really people?), or as an absolutely unnecessary restraint on some aspect of the program that's also inappropriately given static duration. People just need to stop using it so they stop using it wrong.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

I don't understand your point here...
[/quote]
I believe Sir Timothy's point is just because something can be abused, doesn't make it inherently bad. There is some merit to this argument, but I think that there are more shades of grey here. Goto, we all can agree, has rare utility in modern languages - but on those rare occasions it might be the most elegant and maintainable solution.

While there might be similarly rare cases where a singleton is an acceptable solution, I believe in general it is not. Advanced users will (hopefully) have the sense to weigh the engineering trade-offs against one another and make this decision. However, without a specific scenario to discuss, or if we are in For Beginners, I believe that we can avert harm by saying that singletons are evil.


True enough, it is important to practice some self control to avoid overusing them. But just because there's a risk of over-/misuse is no reason to completely discard something.
[/quote]

Arguing that "there is a place for every little thing", in For Beginners, is distracting and can easily send the beginner down the wrong road.

I wouldn't be a fanatic about "no globals". I have in the past used a global for implementing resource caching before. I see caching of resources as an optimisation, not something that necessarily needs to bleed into the high level architecture (though I have implemented it this way too). You can DI this global into the content loading areas of the code, bypassing the need to pass it into this layer.

However, I really feel there is no use for the singleton pattern. You write a bunch more code, which could contain a bug, in order to enforce the "singleness" of the class. As opposed to:

template<typename Resource>
class ResourceCache : noncopyable {
// ...
};

// Later
ResourceCache<Texture> getGlobalTextureCache();

// And finally
ResourceCache<Texture> &getGlobalTextureCache() {
static ResourceCache<Texture> cache;
return cache;
}

Simple, effective, unlikely to contain a bug (multi-threading aside). Just add water.

However, even though I would prefer such a design to a singleton, I still would not recommend it to a beginner.

I don't understand your point here.

Cornstalks and rip-off above are correct, the point was to show that anything is subject to abuse (and quite easily, too), but you don't often hear people say "don't ever use if statements, because it's possible to put more than just comparisons and logic operations in the conditional."


I believe that we can avert harm by saying that singletons are evil.

Okay, I like that FAQ's definition of "evil". It's much better than the people above who say flat out that such-and-such a thing should never be used and there are absolutely no exceptions. There are always going to be exceptions to anything.

In any case, I still stand by my opinion that the OP should use whatever makes the most sense. I'll revise it to emphasize that you should consider alternatives (really, you should probably do this for most design decisions), but if it still looks like the simplest approach to you, do it and see what happens. That's what learning is all about!
My apologies. My bad. I see.

Personally, I won't shy away from singletons (or really, anything) when I feel they are appropriate. An example for me, something I've used in pretty much any project: I generally have some sort of Event Dispatcher, which allows listeners to register for certain event types, and then anyone can fire out events as appropriate. Fairly similar in concept to Win32's message stuff. Pretty useful, I'm sure there are other solutions, but this is the one I like to use, and it has evolved over several projects. It means, for example, that an explosion class doesn't need to get a list of all nearby actors and manually call a dealDamage() function or anything. Instead, damageable actors register for the EXPLOSION event, and the explosion fires the event (either the actors check if they're in range, or I could implement some sort of filter to decide which listeners will actually get the event). Sure, a lot of things end up being connected to the EventDispatcher singleton, but that's never been a problem for me. You might say "oh, but that can't be thread safe!" Well, it's no different than getting nearby actors and calling dealDamage() on each of them. And there's no way I want to pass an EventDispatcher around to every single object I create (not every object uses it, but we don't want to lock that sort of assumption into the code now, do we)


My general opinion is that you shouldn't dismiss something just because everyone says it's bad. I feel pretty confident in saying that any construct has some case where it is appropriate. Sure, there are places where a singleton is far from the right tool, but sometimes it's going to be much cleaner than the alternative. I see several people here saying that singletons are pure evil and should never be used under any circumstance. I say use whatever makes sense. If using a global object or a singleton is going to make your code cleaner and simpler than passing extra context objects around, use it! If down the road you find that you're running into problems with it, then you can change it up. Especially since the OP stated outright s/he's doing this as a hobby, and wants to learn. What could be better for learning what works and what doesn't, than trying it out? If you try something and it works through to project completion, great! If you find that at some point it's no longer sufficient, then you'll understand why it doesn't work and can move to a better solution. Sure, it may take a bit of time to refactor, but the experience is, I think, a lot more valuable that someone just telling you "it's bad, never do it."


A possible solution to having the event dispatcher be a singleton might be to use an object factory. Pass the dispatcher to the factory during its construction and let it handle event registration of game objects. For event firing my first thought would be maybe to use functors to encapsulate the dependency.



I agree that learning hy doing is often the most effective way to learn what not to do, but the OP did ask about singleton (ab)use, so I don't have an issue with the chorus of "don't do it." True global necessity is extremely rare; typically the object is needed in 2 or 3 disparate parts of code, and passing it in feels messy due to some combination of a failure to appreciate the full benefits of passing in dependencies and a need to refactor/redesign the code. Both of these reasons diminish as experience is gained.
I would be interested to read comments on how some of you mitigate duplicate resources without a global-like resource pool pattern that uses reference counting, and so on.

I suppose laungages with GC capability helps in that respect, and so does auto_ptr/shared_ptr to some extent...
Latest project: Sideways Racing on the iPad

This topic is closed to new replies.

Advertisement