• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
neotms

Singleton pattern abuse

45 posts in this topic

[quote]
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 [url="http://www.parashift.com/c++-faq-lite/big-picture.html#faq-6.15"]singletons are evil[/url].

[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.
[/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:
[code]
template<typename Resource>
class ResourceCache : noncopyable {
// ...
};

// Later
ResourceCache<Texture> getGlobalTextureCache();

// And finally
ResourceCache<Texture> &getGlobalTextureCache() {
static ResourceCache<Texture> cache;
return cache;
}
[/code]
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.
0

Share this post


Link to post
Share on other sites
[quote name='Aardvajk' timestamp='1339418350' post='4948139']
I don't understand your point here.
[/quote]
Cornstalks and rip-off above are correct, the point was to show that [i]anything[/i] 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."

[quote name='rip-off' timestamp='1339423750' post='4948160']
I believe that we can avert harm by saying that [url="http://www.parashift.com/c++-faq-lite/big-picture.html#faq-6.15"]singletons are evil[/url].
[/quote]
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 [i]never[/i] be used and there are [i]absolutely no exceptions[/i]. 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!
0

Share this post


Link to post
Share on other sites
[quote name='Sir Timothy' timestamp='1339351410' post='4947982']
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."
[/quote]

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.
0

Share this post


Link to post
Share on other sites
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...
0

Share this post


Link to post
Share on other sites
By having multiple references to a single resource, and passing by reference to where the 'other' references are initialised.

It's essentially the same for smart or raw pointers (though you should prefer const references whenever possible) except that raw pointers are best passed by value. Smart pointers depend on the implementation in question, but the general guideline of preferring to pass by const ref holds.

TLDR: const ref is win.
0

Share this post


Link to post
Share on other sites
[quote]
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.
[/quote]
Load all objects through a common object. This object does not need to be global (i.e. reachable from any point in the code). You might not even need to enforce singularity.

For example, one game I wrote used a per-level "reference stealing" resource cache. Basically, when the first level was loaded, it populates its level-specific cache directly from the disk when a cache miss occurred. When the next level is loaded, a reference to the previous cache was (temporarily) added to the new one. During content loading, resources that caused a cache miss in the new cache were loaded from the older level's cache, and encached in the new one. When level loading was complete, the old cache reference was then removed and disposed of. Any subsequent cache misses would hit the disk (though there should be none during gameplay).

This meant that resources that were common between levels were preserved and copied forward, while level specific resources were disposed of promptly. No globals and no singletons in sight. In fact this approach could not work without the possibility of multiple simultaneous resource caches.

The main downside is that there is a period of time where you have two level's resources in memory at once, but this was a PC game so I was not overly concerned about minimising memory.
0

Share this post


Link to post
Share on other sites
I've been working on a Content Manager (Cache, if you don't like the term "manager") class, for loading in and caching various content files. ContentLoader subclasses can be registered via a template function, to load certain types of file. The manager itself is not a singleton, and not global (may make it globally available some time in the future, but not at the moment), however the way I'm dealing with the loader registrations requires that they be kept in a singleton-esque structure. They aren't really kept in a single structure, but through static fields in a couple of templated structs. So I guess it's not technically following the "singleton pattern", but it does follow the concept of global access to a singular collection of data.

The registration records are only used internally by the CM, and exist until they are no longer needed (I use reference counting along with a static, again singleton-esque, collection of records to ensure they stay alive until all CM objects are deleted and the program explicitly clears the static collection). The overall benefit is that the code to register loaders and to load resources is quite nice, without any extra setup on content types, content loaders, etc:
[source lang="cpp"]// ImageLoader is the default loader for Textures.
ContentManager::RegisterLoader&lt;Texture, ImageLoader>();
// DDSLoader will be used to load "*.dds" files.
ContentManager::RegisterLoader&lt;Texture, DDSLoader>("dds");

// This will use ImageLoader
Texture *tex1 = myContentManager->Load&lt;Texture>("an_image.png");
// This will use DDSLoader
Texture *tex2 = myContentManager->Load&lt;Texture>("another_image.dds");[/source]
Although not shown, each CM keeps a cache of all loaded resources, so that loading the same resource multiple times only does the work once (if a file is loaded once as BinaryData and once as Texture, then it's loaded twice, but that's because it requires different work).

To get the appropriate loader for a resource, there is no runtime lookup into a std::map or anything, I just create a templated struct on the stack based on the resource type (Texture), and that struct gives me access to an existing loader for that type (creates a new one if it doesn't exist yet). As I mentioned, the records are reference-counted, so the loader will be properly freed when it's no longer needed.

A single instance of each loader is used, instead of instantiating a new one for every load operation. If I want to make it multi-threaded, I may add support for creating multiple loaders of a single type, or just make sure the loaders don't maintain internal state.

I imagine I could have written the loader registrations in such a way that each CM could keep its own separate list of them, but it probably would require a bit more work, and really... why? It's not likely that two different CMs are going to need totally different loaders for each resource type, and would require setting the loaders individually for each CM.

I think that having these loader records in a singleton-esque set-up is a small price to pay for ease of use.
0

Share this post


Link to post
Share on other sites
[quote name='Sir Timothy' timestamp='1339351410' post='4947982']
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
[/quote]

I see your point, but the issue is if you need to access one thing all over the place, your design is suboptimal.
For example, in my client, I only have two classes that are accessing the texture manager, that is my OgreView and OgreObject classes. My point is if you give parts of your program certain responsibilities and does not leak those responsibilities outside, you will likely not need to pass around for example a texture manager all over the place. Edited by flodihn
2

Share this post


Link to post
Share on other sites
I prefer to use a factory-like pattern to avoid having to use *Manager classes throughout the code base. In reality implementation relies on CRTP but basically it's objects that exist to create other objects.
For example, if I'm loading in a Material object from disk, I need to grab and store some shared pointers to textures that are referenced in this material. Why the **** should I have to know about how to use a resource caching system to request my texture. Why would I have to make any hard-coded assumptions about which cache that texture might be in? In fact, I don't care at all about some manager or cache that might exist to handle assets, I just want the texture! Not to mention the need to #include and link your managers all over the place grows your compile times.

Here's the point - using the factory pattern you can effectively DECOUPLE the code that uses your assets from the code that manages the cache and prevents duplicates, etc. If you don't use this or a similar pattern, you are tightly coupling your code to the existing texture caching system, so you have not achieved encapsulation and you compromise your code in many many places due to external dependencies. If you are spreading the usage of high level *Manager classes throughout your code, then it doesn't matter whether you use Singleton or dependency injection or public statics, or plain old globals, they are [i]all the same thing[/i] so you might as well just use Singleton and get it over with. Any way you want to justify one method over another, you are still tightly coupled to that interface for that manager.

I suggest stepping away from the notion of master ownership of assets. The bottom line is, the lifetime of an asset should be controlled by a reference count and nothing else - use shared_ptr (and weak_ptr where appropriate). A cache does NOT control the lifetime of an asset directly, however a cache will allow the asset to be released from memory IF the reference count is 1 and it falls out of the LRU list (meaning only the cache itself is holding onto a reference). LRU is only one way to do it, and probably a naive way in most cases, but the point is that if you have a need for that asset somewhere in your frame, it WILL be in memory. If you ever run out of memory or you get disk thrashing, you need to scale back your detail level. Edited by y2kiah
1

Share this post


Link to post
Share on other sites
I use singletons in my game. Not many, but I use them. DebugLogger is a singleton class I have. Every method is protected against multiple threads, and it CAN slow down my performance. Haven't seen it yet, but I know the chance is there. This is only available in debug and debug/release builds of the game. In release, it's not active and doesn't do anything.

It's scenarios like this (and I honestly can't think of another scenario) that are okay to be a singleton. EVERYTHING will need to log something, and you don't want to pass around a pointer/reference to the logger to everything.

Now, what I do with things I know I only need 1 of, but I don't want to make a singleton?

They get held on to by some other object. My RenderSystem class (hands loading and storing textures, animations, models, etc.) is what holds on to the Renderer class. Renderer is something I should only have 1 of...
[source lang="cpp"]public abstract class AllocateOne
{
private static int mAllocations = 0;
public AllocateOne()
{
if (++mAllocations > 1)
{
// assert, exception, log, something to let you know...
}
}

public ~AllocateOne()
{
--mAllocations;
}
}[/source]
I use something like this, and inherit whatever class from it. And, again, I'd only do something like this in debug or debug/release....the final build of your game shouldn't need this type of protection because it should just work. [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]
0

Share this post


Link to post
Share on other sites
I'm not sure why you would make a logger a singleton when using C++ quite honestly. If you did then either you'd be restricted from making a second log file if you wanted to, or if you add that functionality into the singleton you're just adding onto the responsibilities by throwing more management into it.

Also if using C++ and only a single log file I'm not sure why you wouldn't use std::clog() either on it's own or using some small object on the stack that sets the output stream (if using multiple logging locations / types). A singleton is actually a step back from doing something simple like this.
2

Share this post


Link to post
Share on other sites
I would design everything in a way which allows you to have multiple of them so that theyre not connected in any way (maybe not if its some values that need to be calculated that are always the same)
0

Share this post


Link to post
Share on other sites
[quote name='Holland' timestamp='1339642191' post='4949023']
I use singletons in my game. Not many, but I use them. DebugLogger is a singleton class I have. Every method is protected against multiple threads, and it CAN slow down my performance. Haven't seen it yet, but I know the chance is there. This is only available in debug and debug/release builds of the game. In release, it's not active and doesn't do anything.

It's scenarios like this (and I honestly can't think of another scenario) that are okay to be a singleton. EVERYTHING will need to log something, and you don't want to pass around a pointer/reference to the logger to everything.
[/quote]
Look at [url="http://www.gamedev.net/topic/626071-singleton-pattern-abuse/page__st__20__p__4948160#entry4948160"]rip-off's post[/url]. There is *no* need for it to be a singleton. Go ahead, use a global variable. But don't use a singleton. Additionally, you can just use functions, and nothing will need to touch the logger.

[code]
// Thread safe function that logs to a global logger
logThis(const std::string& msg);

void someFunction()
{
logThis("hello world");
}

void anotherFunction()
{
logThis("like how I don't even need to touch a Logger instance?");
logThis("logThis() is a threadsafe function that logs to a global Logger instance");
logThis("but it's nice, because all that is abstracted away. I don't care how logThis() is implemented. I just have a message to log.");
}

void thisFunctionLogsToACustomLogger()
{
Logger logger("customLog.txt"); // open a new log, don't use the global one (this can be useful in certain situations)
logger.log("and look! I'm not abusively restricting myself to having only one logger! I can use"
"another Logger instance if I need, or I can use the global log if I need. Yay!");
}
[/code] Edited by Cornstalks
0

Share this post


Link to post
Share on other sites
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 [i]anywhere[/i] 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.
2

Share this post


Link to post
Share on other sites
[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 [i]anywhere[/i] 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.
[/quote]
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.
0

Share this post


Link to post
Share on other sites
[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 [i]anywhere[/i] 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.
[/quote]
+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] Edited by y2kiah
0

Share this post


Link to post
Share on other sites
[quote name='Washu' timestamp='1339693388' post='4949199']
[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 [i]anywhere[/i] 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.
[/quote]
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.
0

Share this post


Link to post
Share on other sites
[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 [i]anywhere[/i] 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.
[/quote]

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. :)
0

Share this post


Link to post
Share on other sites
[size=3][font=arial, helvetica, sans-serif]Here's the official description of the 'intent' of the design pattern in question…direct from GoF:

[QUOTE]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:

[QUOTE]1. [i]Controlled access to sole instance.[/i] 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.

[QUOTE]2. [i]Reduced name space.[/i] 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.

[QUOTE]3. [i]Permits refinement of operations and representation.[/i] 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.

[QUOTE]4. [i]Permits a variable number of instances.[/i] 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.

[QUOTE]5. [i]More flexible than class operations.[/i] 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][/size] Edited by freakchild
1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0