Skip to main content
GameDev.net gamedev.net
🔒 Locked

Singleton managers

Started by storage Oct 1, 2005 at 12:49 PM 121 replies 16.6k views
Original Post
storage
storage
I'm trying to figure out how to do my managers. Currently I have a manager base class looking like the one in this article, which I derive for every manager I need (textures, models, entities etc). I want some of the managers (textures, surfaces, entities) to only be created once, and let the user create managers for whatever he wants, so I was thinking of making a singleton class, that the derived manager class inherits. Is this a good way to do it? Or is there a better way? Is the singleton class in the Enginuity series good for this? Also, does anyone know a nice Mac OS X IDE? I can't seem to get used to Xcode after using Visual Studio. Sorry if the article is kind of incoherent, I'm a little tired. Thanks in advance!
flangazor
flangazor
Singletons are poor design.

However, if you're commited to using them, then what you're after is not a singleton. You sound like you want an in memory database that loads files and then access for users to add entries into the tables.

With regards to OS X programming, I use XCode for projects on the web that use it, but hand written Makefiles with gVim for my own work. I'm not sure if that's useful for you.
Basiror
Basiror
you could abstract your managers like this

singleton::getinstance(); to access the manager class

and when you need to access your resources you could write a class that hides the use of singletons so you are free to change the way you access your resources later on


class accessorclass
{
getimage(name)
{
singleton::getinstance.loadimage(name);
}
}

like this
http://www.8ung.at/basiror/theironcross.html
storage
storage
Quote:
Original post by Basiror
you could abstract your managers like this

singleton::getinstance(); to access the manager class

and when you need to access your resources you could write a class that hides the use of singletons so you are free to change the way you access your resources later on


class accessorclass
{
getimage(name)
{
singleton::getinstance.loadimage(name);
}
}

like this


Great idea! Thanks a lot! :D
Endar
Endar
Quote:
Original post by flangazor
Singletons are poor design.


Why?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
flangazor
flangazor
Quote:
Original post by Endar
Quote:
Original post by flangazor
Singletons are poor design.


Why?
It's another term for global variables and the opposite of the good design policy of the Law of Demeter. Washu's journal has more: Part 1, 2, 3.
Andrew Russell
Andrew Russell
*rolleyes*

Why is it every time that someone says "Singleton", someone feels the need to pipe in with "Singletons are Poor Design".

I counter your "Singletons are Poor Design" with "Do The Simplest Thing That Could Possibly Work".

If it turns out that eventually you need your manager to be non-singleton, then it's not that hard to refactor it so. But while you're working on it, there's no point in over-designing it (Yag Ni). Singletons are simple and work quite well for resource managers.



To answer the OP's questions:

I use the same templated-singleton method and a similar templated resource method. My system is fairly similar, but with extra stuff like package management, caching, etc. I highly recomend the method you have described.

Similar to what Basiror suggests, my resource managers are actually static objects (rather than singletons) that are in a templated resource class (that each resource implementation inherits). The only place where I actually use the templated singleton is for my package manager.

Just beware that static objects can be optimised out unexpectedly (I put a fake access to the static object in, to ensure its creation).


A final idea: Sometimes I like to use my resource classes without involving the managers (so I can just manually load some files from disk for prototypes). For this I use some preprocessor magic so that I can chose to inherit my resource implementations from an "empty" class that does nothing (but provides the blank functions so it still compiles).
Basiror
Basiror
i have read up some alternatives to singletons

1.) pass the instance you access via single ton as parameter
2.) layout your class hierarchy in a way that you can access it with a function call
3.) use c style functions to access global available services


my opinion a simple accessor interface that decides which way fits your needs would be the only real alternative
a) you can hide the use of singletons
b) you can employ the alternatives mentioned above inside the accessor class without changing the implementation of your probably huge hierarchy

I use singletons due to the easyness of access. In conjunction with the accessor interface I can implement my class hierarchy without coupling of probably independent classes which results in good code that is reuseable and easy to maintain
http://www.8ung.at/basiror/theironcross.html
flangazor
flangazor
Quote:
Original post by Andrew Russell
*rolleyes*

Why is it every time that someone says "Singleton", someone feels the need to pipe in with "Singletons are Poor Design".
Because people are trying to learn how to designs systems and those with experience should guide them away from crap.
Quote:

I counter your "Singletons are Poor Design" with "Do The Simplest Thing That Could Possibly Work".

If it turns out that eventually you need your manager to be non-singleton, then it's not that hard to refactor it so. But while you're working on it, there's no point in over-designing it (Yag Ni). Singletons are simple and work quite well for resource managers.
It is very tedious and time consuming to refactor. I'm sick of having to uncripple code that was, for some insane reason, intentionally crippled and nonregressionable by design.
Quote:

I use singletons due to the easyness of access. In conjunction with the accessor interface I can implement my class hierarchy without coupling of probably independent classes which results in good code that is reuseable and easy to maintain
If you make access easier, then people will be calling the singleton code from all over the system (since it's easy). That increases coupling. Please explain how you decrease coupling through use of singletons because I've not seen this done before.
Nitage
Nitage
Quote:

Quote:

I counter your "Singletons are Poor Design" with "Do The Simplest Thing That Could Possibly Work".

If it turns out that eventually you need your manager to be non-singleton, then it's not that hard to refactor it so. But while you're working on it, there's no point in over-designing it (Yag Ni). Singletons are simple and work quite well for resource managers.


It is very tedious and time consuming to refactor. I'm sick of having to uncripple code that was, for some insane reason, intentionally crippled and nonregressionable by design.


I counter your "Unfounded accusations of bad design" with my "Real-World experience".

Seriously, make a good argument why a logging system shouldn't be a singleton.

Start with why you think you should have more than 1 log interface (I didn't say more than 1 log) and why you think that the logging system shouldn't be globaly accessable.
paulecoyote
paulecoyote
Well may be you want to turn the problem on it's head.

You want some things available to the whole engine? May be you could have one God singleton classand request instances from that.

That way you only have one singleton, and on it's first invokation it can create your texture manager, entity manager, etc classes in there. Doing it that way also gives you control over creation / deletion order and also means those other managers do not have to be singletons.

You might want that God class to be a Monostate rather then Singleton, further simplifying it usage.

As others have said, Singletons can have a damaging effect on your architecture if used too much... causing coupling and dependencies between classes where perhaps there shouldn't be. But some times they and Monostates do make sense. It's just a case of once you expose things like that, it's up to the developer not to abuse it (like having the texture manager directly interfering with the entity manager or visa versa - bluring responsibilities and coupling classes).
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
Puzzler183
Puzzler183
Even if your design is theoretically better than my singletons, guess what... In practice, it's really not. I've tried globals, I've tried singletons, and I've tried the whole "application class" thing. And you know what? For some reason, I keep going back to singletons. They have encapsulation which globals lack, and at the same time I don't need ridiculous things like passing an application class pointer to EVERY function, or doing things like: application->getGraphicsSystem()->getRenderDriver()->enableStencilTest(). Yeah, I'm sticking with singletons because in all honesty, they are just more practical.
JamesKilton
JamesKilton
Quote:
Original post by Puzzler183
Even if your design is theoretically better than my singletons, guess what... In practice, it's really not. I've tried globals, I've tried singletons, and I've tried the whole "application class" thing. And you know what? For some reason, I keep going back to singletons. They have encapsulation which globals lack, and at the same time I don't need ridiculous things like passing an application class pointer to EVERY function, or doing things like: application->getGraphicsSystem()->getRenderDriver()->enableStencilTest(). Yeah, I'm sticking with singletons because in all honesty, they are just more practical.


You keep going back to Singletons because that's the only way you know how to think. Using such objects give you an "It's just there" mentality, thus increasing coupling of your program, and making it harder to test and debug. Frankly the Singleton mentality has become the Microsoft of programming, you have to think quite hard and do some major refactoring to get away from them, but in the end it's a much cleaner, tighter system than before. Heck, I'm still stuck using two in my engine: Logging and Configuration, but that's wrapped up in Object so no-one knows that they are singletons, though I wish there was a better way without the mess of setting up dependency injection. It comes down to the game of "who should know about who." For each singleton you have, ask yourself: "Why would the renderer need access to this?" or "Why would the input manager need access to this?" If you can't answer the question, then that singleton should not exist. It's really not that difficult to pass around a few objects at initialization.

Reasons to not use singletons: Testability, Looser coupling, they _are_ global access points, and maintainability.
flangazor
flangazor
Quote:
Original post by Nitage
Seriously, make a good argument why a logging system shouldn't be a singleton.
Because you may want more than one instance of a logger. e.g. I would like more than one ofstream.
Quote:

Start with why you think you should have more than 1 log interface (I didn't say more than 1 log) and why you think that the logging system shouldn't be globaly accessable.
That doesn't make sense. Perhaps your definition of interface is different from mine: the publically accessible methods of a class or package. How does not forcing a single instance of a class mean the interface will somehow change?
TDragon
TDragon
In response to Nitage's challenge: let's decide what characteristics we want of a logging system.

1. It should be globally accessible. Every function might conceivably need to log a message to help with debugging.

2. It should be accessible at any time, initialized before any other important function is called (including constructors) and closed only when no other important functions remain to be called. See point 1.

3. It should place logged messages consistently, allowing grouping (in a single file, in multiple files by category) to allow ease of reading/browsing

Anything else is icing on the cake, that you can build in yourself.

Going by definition, a singleton will provide a solution to point 1. Again by definition, a singleton will not solve point 2. You need to build that in yourself. Schwarz counters, though complex to program, are one answer among several. A singleton can solve point 3, though by design rather than definition.

I tend to like to keep things simple. Andrew Russell's "Do The Simplest Thing That Could Possibly Work" isn't all-singing and all-dancing, but as it turns out applies well in this case. It's pretty simple to guarantee that no constructors or global functions are called before the logger is initialized; in fact, if your game engine has a base object that contains in some way the rest of the engine's objects (a very common design), all you need to do is initialize the logger, then initialize your engine base. All your logger would need to be is a global ofstream. Do you want encapsulation and some extra sugar? Make the ofstream a static member of a "static" class (e.g. a class that isn't ever instantiated). If your design is complex enough to require it, implement Schwarz counting or some other design idiom to make sure the logger is initialized before anything else that needs it.

You don't need a singleton logging class. The only thing you need to instantiate is the file handle.

Cheers,
Twilight Dragon
Nitage
Nitage
Quote:
Original post by flangazor
Quote:
Original post by Nitage
Seriously, make a good argument why a logging system shouldn't be a singleton.
Because you may want more than one instance of a logger. e.g. I would like more than one ofstream.
Quote:

Start with why you think you should have more than 1 log interface (I didn't say more than 1 log) and why you think that the logging system shouldn't be globaly accessable.
That doesn't make sense. Perhaps your definition of interface is different from mine: the publically accessible methods of a class or package. How does not forcing a single instance of an object mean the interface will somehow change?


By "more than 1 log", I meant more than 1 log file.
By "more than 1 log interface" I meant more than one way of accessing those logs.

I was using the common English meaning of interface (rather than the specific OO meaning).

Quote:

I counter your "Unfounded accusations of bad design" with my "Real-World experience"


I don't mean to imply that you have no experience - mearly that the only arguments that you have advanced are based on academic theory rather than real-world use.

Such arguments very often tend to be inaccurate - in this case, you, and others arguing the same case, have repeatedly asserted that the advantages of using singletons are trivial compared to the disadvantages wihtout providing any evidence.

Do you know what the OSI Reference Model is?



Nitage
Nitage
Quote:

A singleton will not solve point 2.


Singleton is an Object-Oriented design pattern. The static instantiation order problem is peculiar to C++ and not a result of the definition of the singleton pattern.


Quote:

I tend to like to keep things simple. Andrew Russell's "Do The Simplest Thing That Could Possibly Work" isn't all-singing and all-dancing, but as it turns out applies well in this case. It's pretty simple to guarantee that no constructors or global functions are called before the logger is initialized; in fact, if your game engine has a base object that contains in some way the rest of the engine's objects (a very common design), all you need to do is initialize the logger, then initialize your engine base. All your logger would need to be is a global ofstream. Do you want encapsulation and some extra sugar? Make the ofstream a static member of a "static" class (e.g. a class that isn't ever instantiated). If your design is complex enough to require it, implement Schwarz counting or some other design idiom to make sure the logger is initialized before anything else that needs it.


Your solution restricts you to having nothing use the log before you initialise it - no static or global objects can use the log in their constructor.

In other words, your solution has exactly the same defect as a standard C++ singleton, with the added disadvantage that any method, anywhere in the code can call close() on your ofstream, completely breaking your log.



EDIT: By the way, it is possible to work around the static constructor problem in C++ using a singleton (in a way that is totally invisible to users of the class btw).
TDragon
TDragon
Quote:
Original post by Nitage
Singleton is an Object-Oriented design pattern. The static instantiation order problem is peculiar to C++ and not a result of the definition of the singleton pattern.

True, but a singleton still doesn't solve it. You need something more. That was my point.


Quote:
Your solution restricts you to having nothing use the log before you initialise it - no static or global objects can use the log in their constructor.

In other words, your solution has exactly the same defect as a standard C++ singleton, with the added disadvantage that any method, anywhere in the code can call close() on your ofstream, completely breaking your log.

Please, read a bit further on to where I mention using a static class for encapsulation. Then note where I mention using a method such as (but not limited to) Schwarz counting. Both of these methods solve the problems you mentioned without using a singleton; neither is solved by using a singleton as-is.
flangazor
flangazor
I'm still not sure what you mean by interface then. As TDragon said, all you need are file handles for the logging which can be threaded, globally accessible, or aspects of a package. There's no reason to force only one instance of a logging class.

WRT OSI, yes I'm familiar with it. Yes, I'm familiar with Torvald's recent flame of OSI style specifications. No, I'm not putting forward acedemic reasons. I'm describing my frustration with having to refactor singletons from places where there was no reason to *force* a single instance of a class when merely instantiating a single instance of a class would have done nicely.
Nitage
Nitage
Quote:
Original post by TDragon
Quote:
Original post by Nitage
Singleton is an Object-Oriented design pattern. The static instantiation order problem is peculiar to C++ and not a result of the definition of the singleton pattern.

True, but a singleton still doesn't solve it. You need something more. That was my point.


Quote:
Your solution restricts you to having nothing use the log before you initialise it - no static or global objects can use the log in their constructor.

In other words, your solution has exactly the same defect as a standard C++ singleton, with the added disadvantage that any method, anywhere in the code can call close() on your ofstream, completely breaking your log.

Please, read a bit further on to where I mention using a static class for encapsulation. Then note where I mention using a method such as (but not limited to) Schwarz counting. Both of these methods solve the problems you mentioned without using a singleton; neither is solved by using a singleton as-is.


But the problem isn't caused by singletons - it's caused by oddities in one paticular language. The method you proposed suffesr form the same problem and the solutions you proposed could be applied either to your proposed method or singletons.

All you've shown is that implementing a singleton in C++ isn't as easy as it
looks.

Global File Handles lack encapsulation - rogue code can erase previous log entries, close the log file etc.

Using a class with all static members increases the potential for mayhem in C++ - if you have more than one static data member you don't know what order they are instatiated in. In a singleton you only have to deal with the one instance pointer.




Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.