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

Avoiding singletonitis

Started by yzq89 Nov 16, 2005 at 12:59 AM 35 replies 7.2k views
Original Post
yzq89
yzq89
Hi all, I'm designing my game engine and this is something that keeps popping up. I currently have a CTexture class that encapsulates texture-related functions (loading, binding, unloading) and a CTextureManager class that releases the textures at application termination. The texture manager is held by the graphics engine class. Now, if I want to load textures, how should I go about doing it? For example, my window class wants to access a texture for the window skin. However, the class hierarchy does not allow for passing a pointer (to the texture manager) to the window's loading function. My class hierarchy looks like this: CEngine -CGraphicsEngine --CTextureManager ---CTexture -CUserInterface --CWindowFactory (creates CWindow) --CWindow Similarly, I have a map editor window class (which extends CWindow) that needs access to the map/scenegraph class. How can I pass a pointer/reference, to the map class, to the map editor window? The straightforward workaround is to make both the texture manager and map class singletons. However, I know that not every piece of code will need to load textures, or access the map. So I just wanted to run this by you, am I missing something, or should I reconsider my class hierarchy and how? Thanks in advance
Barius
Barius

The CTextureManager is a good candidate for a singleton, but I think it would be more flexible if it wasn't directly accessed by your window class, but rather by the window factory. The factory would call a method, say CWindow::setSkin(Texture*), to set its texture. CWindow won't need to know about the texture manager this way.

Making the map a singleton is bad, since you might want to keep several maps in memory at the same time. Whichever class creates map editor windows should pass the appropriate maps to them.
dave
dave
I pracically never use the singleton pattern. When i find that there is no other option but to use it then i know i'll be using it in proper context.
paic
paic
Hi,

If your CWindow class need access to textures, then your texture manager simply isn't in the right place. Put it in the CEngine so that it can be addressed by the class of the graphic engine and the user interface. Or use a singleton, the result probably would be the same (I like singletons ^^)

Another option is to allow 2 instances of the texture manager, one for the graphic engine, and one for the user interface. Obviously, the textures will be mainly different for both parts so there shouldn't be too much problems with redundant textures.
Tesseract_Hotplate
Tesseract_Hotplate
There's no reason to avoid what works. And there is nothing intrinsically wrong with singletons. If anything they enforce the concept of a 'manager' providing a 'service'. And it should help you prevent deeply coupling any client of that service to the manager.

So, in short, don't shy away from singletons.
JD
JD
I like to pass objects around thru methods because it's more eviden of what's going on. So in your case, the window would go thru the engine to the texture manager to get the texture it wants. I suppose the window for speed reasons could house the pointer to the texture as well rather than ask it each time especially when you have a long texture list to avoid walking it at runtime. The reason for singletons is so that the construction order of global objects could be controlled. It's necessary because objects are instances while in C one would use functions not objects. When every global object is made then the main() starts invoking all the init() functions for all modules and you can then controll the init of individual modules that way.
DirectXFreak
DirectXFreak
Why not use global friend functions?
For instance, on my resource manager I have two functions that have access to it's private data:

safeLoadResource(...)

and
safeFreeResource(...)


This allows the functions to be used anywhere in the application, but still hides the manager function.
--m_nPostCount++
owl
owl
I also have a manager for my textures, which happens to be a singleton (it is a static member of my renderer class actually). My texture classes can load themselves, so there are special places where I don't need the manager and I just drop a ctexture class there and use it.
[size="2"]I like the Walrus best.
Spoonbender
Spoonbender
Quote:
Original post by Tesseract_Hotplate
There's no reason to avoid what works

Except if something else works better, or will be easier to work with in the future, or will be easier to debug, or....

Quote:
If anything they enforce the concept of a 'manager' providing a 'service'.

If anything, they enforce the concept of globals that go against any OO-ness your current design might have. (Of course, that's not in itself a problem, but given that your class hierarchy looked object-oriented to me, and you're worried about which classes should be able to access what where, I'd say it's a valid issue)

Quote:

And it should help you prevent deeply coupling any client of that service to the manager.


Allowing *anything* to use the texture manager *anywhere* helps you prevent coupling? That's news to me...

I generally only use singletons when a) something needs to be accessed literally *everywhere* (Good candidates might be a class for logging, or maybe one keeping track of config settings). If I'm making something that's only of use to one or two classes, then those one or two classes should just be given a pointer to the class.

But anyway, how come your window class needs access to the texture manager? Doesn't sound very logical to me. I'd either keep the window skin separate from actual game textures, or I'd let the CGraphicsEngine handle it (Or some other class under it).
owl
owl
Quote:
Original post by Spoonbender
But anyway, how come your window class needs access to the texture manager? Doesn't sound very logical to me. I'd either keep the window skin separate from actual game textures, or I'd let the CGraphicsEngine handle it (Or some other class under it).


It depends. I usually let all my graphical objects draw themselves when there is no real need to sort them by texture. If you were drawing a window with quads and you wanted to texture it, you could have your window class to activate the texture it needs directly and then upload the vertices to the renderer. All in it's (say) Draw() method.
[size="2"]I like the Walrus best.
CJM
CJM
Hey,

[rant]
In my own project, I go one further along the 'road of what not to do'. I mean, why use singletons when you can use static globals. Personally, I only have about 8 such objects [simply because I find it more modular to describe my sound class only within files that concern the sound class].

My sound system ideally should register itself with a global system manager, which ensures that the system is initialised and uninitialised in the correct places [it's not quite there yet for several reasons, all based on time available], but take away the files describing sound and my system doesn't have sound. Replace them with a new sound system, and my system will use that.

I'm yet to see any other implementation which can literally add/remove/replace blocks like my system can. Yes, there are issues, particularly if other code [read game code] relies on any subsystem that isn't available [and those are either linker errors if a system's been referenced directly, which shouldn't be the case], or logged runtime errors [unless the system has been redefined].

No, my code isn't generally considered good OO practice. But there's nothing that says that the other schools of thought automatically hold no merit. But on the other hand, my engine works in a way that doesn't so much lend itself to encapsulating everything into a fixed 'Engine' class anyways.

[/rant]

That said, you've got yourself a pretty complicated problem here methinks because I don't see any sane way to provide the window skin to CWindow with the structure that you currently have. The problem is that you have two different types of data belonging to different systems which, at least in part have common data between them.

Perhaps I would consider having a CWindowTexture which could be created from a CTexture through an external conversion class. Of course, to do that efficiently, you need the conversion class to be accessable to all objects, and also have knowledge of all objects [which probably means some sort of global resource pool].

The easiest solution [to your texture issue] IMO is to provide the raw texture loading functions to both the windowFactory and the textureManager through an intermediary class higher up in the structure. Or provide a class which can obtain and convert CTexture objects to CWindowTexture objects.

The solution to the map editor and map class problem is somewhat more complicated. Again, I would provide an intermediary class which had knowledge of the map class to the map editor class.


Of course, I know that my thoughts may not be the most popular way of doing stuff, and I understand that they may not be the best way, so take them with a grain of salt. These are just some of my initial thoughts on the problem - feel free to ignore them, particularly because I'm no OO expert.

--CJM
Scarfy
Scarfy
Could someone explain to me why people have such a hang up about using Singletons?

Honestly, shouldn't you just use a design pattern that makes sense and gets the job done?

I've seen people say that Singletons should be avoided and are bad design. Frankly I can't see why?

(Note - This isn't a rank or a flame, I'm just wondering).
Parallel RealitiesProject: Starfighter
Nitage
Nitage
To answer your question, your CWindow should have indirect access to the scenegraph by claling a function in your graphics engine which renders the scenegraph. If CWindow has code that manipulates the scenegraph instead of just rendering it then that code is in the wrong place.


And on singletons:

From the GoF book, the intent of the singleton pattern:
Quote:

Ensure a class only has one instance, and provide a gloabl point of access to it


Q: Why would you tie together a class which you can only have one instance of and a class which has a global point of access?

A: Because it's much easier to implement a class which can only be instantiated once if that class also has a global access point.

In other words:
Singletons are a product of C++ not of OO design.

I've never come across an example of a class where it was appropriate to have a global access point AND be restricted to a single instance.

A log system is the closest I've ever encountered, but then it's usual to have multiple logs - one for each subsystem.
TheFez6255
TheFez6255
Quote:
Original post by Scarfy
Could someone explain to me why people have such a hang up about using Singletons?

Honestly, shouldn't you just use a design pattern that makes sense and gets the job done?

I've seen people say that Singletons should be avoided and are bad design. Frankly I can't see why?

(Note - This isn't a rank or a flame, I'm just wondering).


Generally I shy away from them unless I need them (ie an error logging system, etc). However there are some major flaws that can arise with them. Most notably memory problems (note that I'm not just talking about in C++). This is especially true with threads, DLLs, etc. Anything with seperate heaps can cause problems. Also note that certain OSes may allocate memory differently, making the problem even more difficult to pin point. Theres a bunch of other potential problems, but if your smart then it shouldn't be difficult to fix. A decent article can be found here on the subject: Problems with singletons
Conner McCloud
Conner McCloud
Quote:
Original post by Anonymous Poster
Quote:
Original post by Nitage
A log system is the closest I've ever encountered, but then it's usual to have multiple logs - one for each subsystem.


Could be desirable, but I've never played a game or seen an application that does this. There is no gain to splitting a log by subsystem. If anything it makes things harder to follow, as you can't easily see the ordering of events when they are spread across several files.

Tie the logs together by default, but provide a way to untie them should you need information on only certain parts.

Don't get hung up on trying to 'avoid' bad design. The right way to do things will come from experience...going out of your way to make things more difficult just means you won't ever get your project done. What's worse: a project filled with bad design choices, or an project that's permanently stuck in the design stage?

CM
paulecoyote
paulecoyote
The hardest part to get right when designing classes is correctly diving responsibilities between your concepts.

Singletons can muddy that water some, and are implementation detail. Think to yourself, do you really need to get to that class instance from everywhere? The answer is almost always no - because if you design things right only things that need to know about a Texture manager can access it, from everywhere else it is hidden.

For example if we were doing a simulation of a hand, a hand only has one thumb. A thumb is used everywhere - it's really versitile, so perhaps you decide to make it a singleton. Yet a thumb is pretty useless without the rest of the hand, but potentially it's life time is now independant because it's a singleton - eg it may exist before the hand, and after it's gone. And what if you suddenly need 2 thumbs? Or another hand? Requirements have changed and your shackled to your existing implementation. If it were just another part of the hand, then that wouldn't be the case. True now request to thumb have to go through hand, but that's the way it should be. If you simulated a foot as well, it could potentially go right for the thumb - which unless you were doing reflexology or something wouldn't make much sense [wink]

Flip the problem on its head, consider how you would like to use the engine. Consider using Agile / XP methods... program code that will use your engine before you actually write the functions - rather then trying to anticipate how the engine will be used and potenially programming a ton of functions that are unnecessary or unhelpful.

For the responsibility assignment, think about using something like CRC cards to create your hierachy.

On a side note, does warting classes with a "C" annoy anyone else? I didn't used to mind, but recently it's been bugging me more [smile]
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.
ApochPiQ
ApochPiQ
I've found two principles extremely handy when solving design problems, especially those involving singletons and avoiding global-variable-syndrome:

1. The mediator pattern is your best friend. Don't be afraid of adding chains of classes that do little but translate and relay requests to different subsystems.

2. Have your functionality classes (as opposed to glue/request dispatching classes) talk to as few external objects. The Law of Demeter has some decent suggestions about how to accomplish this, although in practice it is better to favor clean and understandable code over pedantically LoD-correct code.


One thing to think about when considering a singleton is whether or not you really only want one object created. Ask yourself these questions:

- Do I really need to limit the instances of this class to one, or do I simply only foresee a need for one instance? Avoid design decisions that limit you to assumptions that may become invalid later - i.e. don't use a singleton if you do not absolutely need to limit the class instances.

- Do I really need to limit the instances of this class at all? For instance, if the class wraps access to a restricted resource that my program can only open once (e.g. a writeable file handle), do I really need to limit instances of the class, or does the class need to transparently limit its own behavior? A variation of this question that might be more illuminating: do users of this class need to understand that there are a limited number of instances of the object available, or is it better that they can access the resource at will and let the class itself handle the limits? Remember not to expose implicit assumptions about your objects' behaviors in the design; it's better for the class to totally hide the instance-limited behavior than to impose artificial constraints on what the clients are allowed to do with the class.

- Do I need to have precisely one object, or do all of my objects access common data? If you do not strictly need to have precisely one instance of your class, then use static member data in the class to share it among all instances, or refactor the data storage into another separate class and use your would-be singleton as a mediator to access that storage.



I recently did a big project rewrite that involved a massive refactoring from a singleton-laden mess of hideously bad code; it was hacked together by someone severely lacking in real-world design experience, and was built almost entirely with MFC and the Visual C++ 6 ClassWizard. (I still have nightmares.)

The old design was full of assumptions: we'll only ever make one connection to the data server, so let's hold a global pointer to it. Oh, except global variables are bad, so we'll cast the result of ::AfxGetApp() to our CWinApp subclass and retrieve the pointer from a public member variable there. A user has to be logged in for the system to function, so all of the behavior should be encapsulated in a CUser class. Except later the requirements changed and allowed limited access with nobody logged in, and so all of the encapsulation had to be very brutally violated. This kind of thing was rampant in the code and made it a total nightmare to maintain.

When I came along and rewrote the system, it ended up designed from a totally different perspective. By operating on the two principles I listed at the beginning of this post, I was able to get a much cleaner design, and actually rewrote fairly little of the important logic. All system operations are routed through a set of tightly coupled "request dispatch" monostate classes. These essentially talk to only one layer of code: service managers. The service managers in the current system are the UI manager; the server connection manager; a utilities manager for things like registry access, local file caching, etc.; and the policy manager. The policy manager is the more interesting, as it was put in place specifically to counter the use of singletons. Essentially, requests that must be checked against certain security settings are handed to the policy manager. The appropriate policy is retrieved from a CPolicyAccess monostate object each time the policy must be checked, which allows for the policy system to become as fine-grained as the end-user code that checks the policy settings if that is ever needed. A separate and highly decoupled CUserSession class mediates between the policy management subsystem and the server, using another layer of functionality classes to do the actual socket communications and protocol wrapping.

The end result is that any client in the system only needs to know one class: the request dispatcher for the family of functionality it handles. This has already paid off several times, where swapping out entire chunks of the system can be done trivially without even having to modify any other code. I've actually already converted a couple of monostate objects to more robust classes in the system; doing so required changes only to the monostates themselves, and the client code didn't even have to be touched. In the old system, where singletons were used instead, a similar change took several days of rewriting and debugging client code that was built on implicit assumptions. Most of the time, the original programmer wasn't even aware that he'd made the assumptions in the first place.


The c2 wiki page on singletons has more very interesting thoughts on when and when not to use the pattern. I'd highly recommend that anyone working with OO design read through it (and the rest of the patterns discussions there, for that matter).
dotproduct
dotproduct
I think this "avoid singleton's at any cost" is a bit overdone...

If you use them correctly, they do what they're supposed to very nicely:
"Ensure a class only has one instance, and provide a global point of access to it."

Edit: I totally agree with ApochPiQ that the Mediator pattern is extremely handy, it can do ALOT for encapsulation, and defining an interface to a subsystem through only one class/object.
DigitalDelusion
DigitalDelusion
Quote:
Original post by paulecoyote
On a side note, does warting classes with a "C" annoy anyone else? I didn't used to mind, but recently it's been bugging me more [smile]


Actually I think it can carry usefull information but I use the following prefixes:

i - interface
c - concrete
- (nothing) plain data holders (I've not made up my mind if postfixing _t is a better option I do use that in C)


But maybe that's just me.
HardDrop - hard link shell extension."Tread softly because you tread on my dreams" - Yeats
ApochPiQ
ApochPiQ
I got into the CClass warting thing back when I first started on C++ (which, sadly, was in MFC, where everything is warted horribly). Thankfully I've long since given up most of it, but I still find a few warts highly useful at times:

- pFoo for pointers
- IFoo for interfaces
- m_Foo for internal (private or protected) class member data
- s_Foo for static class member data, regardless of access level

I really only do it because it's a slight mnemonic habit, and I do a lot in IDEs (and raw text editors... urgh) where thinks like mouse-over class information are not available. At the moment I still use CClass because that's the convention in the code I work with all day, but for my own purposes I simply use well-chosen TitleCase names.

Topic Locked

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

Sign in to reply to this topic.