Singleton pattern abuse

Started by
44 comments, last by freakchild 11 years, 10 months ago
Actually I would suggest writing your logger in C. It's the craziest thing -- for example, you can write to stdout using printf or fprintf or fputs from anywhere in your code, at all! And they didn't use any singletons at all either. Who knows how they managed to pull off that wild trick? Oh right, it's because we had the ability to access global values safely before the GoF book burst in like Seinfeld's Kramer looking bewildered at its own discoveries.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Advertisement

Actually I would suggest writing your logger in C. It's the craziest thing -- for example, you can write to stdout using printf or fprintf or fputs from anywhere in your code, at all! And they didn't use any singletons at all either. Who knows how they managed to pull off that wild trick? Oh right, it's because we had the ability to access global values safely before the GoF book burst in like Seinfeld's Kramer looking bewildered at its own discoveries.

Best of all, you don't even have to expose the "globals" those use, just the functions that use them. Thus you've contained your globalitus into a single compilation unit.

Or you could do it the C++ way, like std::cerr and std::clog, if you need a slightly more useful interface. No singletons there either.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.


Actually I would suggest writing your logger in C. It's the craziest thing -- for example, you can write to stdout using printf or fprintf or fputs from anywhere in your code, at all! And they didn't use any singletons at all either. Who knows how they managed to pull off that wild trick? Oh right, it's because we had the ability to access global values safely before the GoF book burst in like Seinfeld's Kramer looking bewildered at its own discoveries.

+1 to that, I've always found this to be enough for my purposes
[source lang="cpp"]
#if defined(_DEBUG) && defined(DEBUG_CONSOLE)
#include < cstdio > // for printf
#include < tchar.h >
#define debugPrintf(...) printf(__VA_ARGS__)
#define debugWPrintf(...) wprintf(__VA_ARGS__)
#define debugTPrintf(s,...) _tprintf(TEXT(s),__VA_ARGS__)
#define ifDebug(...) __VA_ARGS__
#else
#define debugPrintf(...)
#define debugWPrintf(...)
#define debugTPrintf(...)
#define ifDebug(...)
#endif
[/source]

[quote name='Promit' timestamp='1339690904' post='4949187']
Actually I would suggest writing your logger in C. It's the craziest thing -- for example, you can write to stdout using printf or fprintf or fputs from anywhere in your code, at all! And they didn't use any singletons at all either. Who knows how they managed to pull off that wild trick? Oh right, it's because we had the ability to access global values safely before the GoF book burst in like Seinfeld's Kramer looking bewildered at its own discoveries.

Best of all, you don't even have to expose the "globals" those use, just the functions that use them. Thus you've contained your globalitus into a single compilation unit.

Or you could do it the C++ way, like std::cerr and std::clog, if you need a slightly more useful interface. No singletons there either.
[/quote]

I'm curious, how are these different from having a singleton Logger class? Something like printf() has singleton-like behavior, you can't have multiple versions of printf() that have different output locations (fprintf supports this, by explicitly providing the stream, but I'm talking printf since it was also mentioned). Similarly, std::c[out|err|log] always write out to the same location. I believe in both cases they can be redirected to any other stream, but it still globally affects it; a singleton Logger could support redirection as well.

Building a class around printf, std::c[err|log|out], etc would allow you to change your underlying logging system in a single place, instead of anywhere you log a message. Or, if you don't like classes, wrap with a bunch of global functions, it'll be pretty much equivalent if the class is a singleton.

Okay, so Logger doesn't technically need to be a singleton, just providing a global instance would be sufficient for most needs, with the possibility of creating additional special-purpose Loggers. This would make it more powerful than just using printf() or std::cout, since those behave similar to singletons.

Actually I would suggest writing your logger in C. It's the craziest thing -- for example, you can write to stdout using printf or fprintf or fputs from anywhere in your code, at all! And they didn't use any singletons at all either. Who knows how they managed to pull off that wild trick? Oh right, it's because we had the ability to access global values safely before the GoF book burst in like Seinfeld's Kramer looking bewildered at its own discoveries.


In a prior life, I worked on a teller app for a major bank, and at the point I joined the team it was already an extremely mature code base. While working on it, we were noticing extreme slow downs during certain times of the day.

In the end, tracing it back, the culprit was fprintf logging calls, which were horrifically slow, especially on OS/2. I ended up altering it, I forget the details, but it results in about a 60% across the board increase in application speed.

That has little to do with the story at hand, but is a cautionary tale about fprint usage. :)

[font=arial, helvetica, sans-serif]Here's the official description of the 'intent' of the design pattern in question…direct from GoF:

Ensure a class only has one instance, and provide a global point of access to it.[/QUOTE]

Everyone seems to know this. I just provide that more as reference than anything but I note there are really two parts of the intent…some people get hung up about the whole global part and miss the ‘one instance’ bit, but many folk of course don’t too.

Now, one by one the so called 'consequences' as documented by GoF:

1. Controlled access to sole instance. Because the Singleton class encapsulates its sole instance, it can have strict control over how and when clients access it.[/QUOTE]

Controlling access is fine. I understand the comment on how the ‘how it is accessed’ is controlled but I don’t quite understand the comment regarding the ‘when’. According to the pattern any code that has access to this can do so when it wants. The only mechanism related to time that I am aware of is that the point of first access ‘may’ also be the initial construction. Even if you are controlling the construction to the point of first use and access, you may not know when that occurs unless of course you manually force it before any other code does.

Either way, I don’t think this is really an interesting ‘consequence’. Nothing wrong with it, but I don’t feel it helps to justify the pattern.

2. Reduced name space. The Singleton pattern is an improvement over global variables. It avoids polluting the name space with global variables that store sole instances.[/QUOTE]

Okay. Fine. At least it is an improvement over the global, but it’s very marginal one at that. One could argue it maybe feels a little better, a little cleaner…but then again the general unpopularity of the Singleton might actually suggest a global is more easily and widely acceptable for cases where there is felt to be no other option.

Personally I don’t think this really adds much justification for the existence of the Singleton.

3. Permits refinement of operations and representation. The Singleton class may be subclassed, and it's easy to configure an application with an instance of this extended class. You can configure the application with an instance of the class you need at run-time.[/QUOTE]

This is a more interesting 'consequence' which on the surface seems a little more useful than #1 and #2. I wish they'd have elaborated on this a little more in the book though because ‘as is’ I think it’s open to some interpretation. At least I think the methods where one configures an application with an instance of this extended class might generally be other unpopular techniques so I wish they’d provided an example of it. If there was a good clean option for this it might actually get around some of the dependency issues Singleton’s proliferate, which could address quite a lot of criticism.

I would question however why you might want to subclass a Singleton? I can only assume this would be to return a more specialized version of the sole instance you’re wrapping. That seems a little closer to ‘factory’ type functionality that might be focused on a single instance. Of course it could be argued that a Singleton is a single instance factory really, but then the whole topic of ‘what happens when you need more than one’ crops up still.

4. Permits a variable number of instances. The pattern makes it easy to change your mind and allow more than one instance of the Singleton class. Moreover, you can use the same approach to control the number of instances that the application uses. Only the operation that grants access to the Singleton instance needs to change.[/QUOTE]

I find this interesting to think about, if only because it appears to address some of the ‘what if you need more than one’ criticism. It’s almost as if GoF saw this coming, or at least are acknowledging the issue in some way.

Of course, they are right…if you have a Singleton it isn’t too hard to modify it to address this common criticism. Now at that point it will no longer be a Singleton of course, so this particular ‘consequence’ doesn’t justify the case for the Singleton either. After all, any code can be modified and refactored with varying effort – changed from one thing to another...so what are they really saying here - 'this has the same advantage that every other pattern has?'

On the other hand, you could argue that if you were to follow this path of multiple instances then you just lose one half of the intent. Unfortunately that half is the ‘single instance’ bit, which leaves the remainder as just a fancy way of getting at a global.

5. More flexible than class operations. Another way to package a singleton's functionality is to use class operations (that is, static member functions in C++ or class methods in Smalltalk). But both of these language techniques make it hard to change a design to allow more than one instance of a class. Moreover, static member functions in C++ are never virtual, so subclasses can't override them polymorphically.[/QUOTE]

The way I read this is one of acknowledging the alternatives to a Singleton class – getting to the same objectives by different means (i.e. not using ‘the pattern’). Again this is something that often comes up in these arguments against the Singleton. The text goes on to explain why a Singleton is a better alternative though. I do think there’s some truth and fair comment behind this ‘consequence’, but you can also say the same about the alternatives.

I thought I would post some of the official text because quite a lot of people may have never read it and could be interested. If you have read it, you’ve likely forgot it or don’t care to remember too.

Note: I really should know better than to join in any discussion of this topic.[/font]

This topic is closed to new replies.

Advertisement