• Advertisement
  • entries
    146
  • comments
    436
  • views
    198064

Singletonitis, Part 3

Sign in to follow this  

298 views

Quote:
Original post by 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 ?


Could it be? Do my eyes deceive me? Or did someone actually get the point of my post? I'll answer your question in a bit stormrunner, but first...

Quote:
Original post by _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


I am of two opinions to that idea. The first is: KEEL Phantom. However, the second is much more interesting.
[Edit: Over the months/years I've looked back at this and contemplated fixing this. Frankly, I don't use singletons for logging, as it's not a flexible solution that can be easily removed when porting code. There are far cleaner solutions that I would suggest implementing, but I won't cover them here.]
You wish to log events, such events might be informational, warnings, errors, and debugging information. How can we accomplish such? The first, and obvious choice would be to make a logging singleton with a function that would take a state flag...something like: Write(LL_DEBUG, StringBuilder("2 + 2 = {0}") % result);. This seems like a fairly reasonable approach, and would be perfectly acceptable for an entry level logging system. But, lets make our functional requirements a bit more complex. First, we would like the ability to direct the logging system to multiple streams. You should be able to indicate the logging level for each stream. The levels are arranged in a hierarchical structure, like so:

  • Debugging

  • Informational

  • Warnings

  • Errors


So, if you select the Warnings level, your stream will receive Warnings and Errors, nothing else. Obviously, if you select the Debugging level then you are going to receive everything. So, here's the question, is the new design still suitable for a singleton? We'll, actually, yes. Does it have to be? No, but in this case I would consider it an appropriate use of the singleton pattern. Quite simply, you will most likely be accessing the logging singleton all over the code. And refactoring it out is not an option.


Another possible singleton would a concrete factory that you expose from a DLL via an interface (as shown in the UML diagram below). In this way the actual implementation details of the classes would be hidden from the client. This would also enable you to control the number of times that the factory allows the provided interfaces to be instantiated. In this way you can achieve your goal of guaranteeing only single instantiation of various types (Graphics object, Input object), but when you need to allow for multiple instantiation, you aren't bound to a singleton object, instead you just use the factory interface to obtain another pointer.


That should also answer Etherstar's question.
Sign in to follow this  


7 Comments


Recommended Comments

I am now seriously considering creating a concrete factory now for my current game. I'd get the encapsulation I'm looking for with the scalability for the future. Not to mention it looks a heck of a lot cleaner with a single interface.

Share this comment


Link to comment
I agree with most of your points, but I wonder at what state does a concrete factory become ...a singleton wrapper? You have a few abstract classes / interfaces, and you are ensuring that only one instance is created of each...and you have one global ConcreteFactory? But now wouldn't you have to be making function calls all over your code for ConcreteFactory::, and therefore you run into the same issue you had when you were creating a singleton in the first place, do you not?

If you ask me, passing pointers "downstream" is the best way to go...

I have really enjoyed the last couple of posts, though! Well written!

Share this comment


Link to comment
Your code doesn't call the concrete factory like that. It uses the interface to the factory that is provided.

IFactory *f = GetFactory();
IGraphicsManager* graphics = f->CreateGraphicsManager();


Hence your code doesn't actually know that the concrete factory is a singleton, although it might "assume" as much. In general, the factory interface will be used in only a small portion of the client code, since you shouldn't be creating such objects all over the place (although, in the case of an editor where you use the factory to create widgets...)

Share this comment


Link to comment
I am speaking specifically about the "GetFactory()" method. Would you just create that as a global method? Why not make the factory a class? In which case, dont we go back to the same singleton issue?

I might simply be misunderstanding you, and if that is the case, my appologies.

Share this comment


Link to comment
The GetFactory() function is a function because it must return a pointer from a DLL. Hence you must be able to GetProcAddress() it. The factory IS a class, which should have been evident through my usage of it.

Share this comment


Link to comment
Ah, my apologies -- I didn't recognize that you were calling it from a DLL. All makes sense now. Thanks.

Share this comment


Link to comment
There is one case you've not mentioned where you do need a singleton.

If you have code that is running before the C Runtime Libraries have started up and called main(), and this code depends on another system having alredy been started up (logging system would be an exaple, but there are many), then that system needs to be a singleton because the order of construction is undefined here.

This would be code running in the contructors of global objects for registration purposes etc.

The good thing is that at this stage there will only be 1 thread running, so you dont have to worry about threading here. But the problem is that the order of destruction is also undefined, and if a singleton is deleted by the c runtimes and you still need it due to code in your destructors then you're going to have problems. Sometimes its hard to figure out whats going on and why your code is crashing.

The solution is to use singletons that enforce an order of destruction. The nifty counter singleton pattern will do this. This pattern basically uses reference counting on the singletons for deletion.

Share this comment


Link to comment

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

  • Advertisement