View Full Version : Singleton vs. Object??
wazoo
11-23-2004, 05:34 AM
Goofy subject title (sorry 'bout that), but I'm not sure how else to title this thread...;)
Currently, I'm coding my game with DX8.1 and am trying to figure out the "best" way to handle individual objects in my world scene (I call them "Actors").
My GraphicsManager object contains the Direct3D specific pipeline stuff (ie. hashmaps of textures, vertex buffers, fonts) as well as the Direct3DDevice interface.
Now my REAL question (I'm getting there) is is it better to create the GraphicsManager as a singleton object and use it within each Actor's render method, or is it more efficient to just pass around the GraphicsManager object as a parameter to the Actor's render method...
some sample code might clarify...
//Design A: current design
class GraphicsManager {
//D3D8.1 specific stuff
};
class Actor {
public:
virtual void renderActor(GraphicsManager* pManager){
//do kickass graphics stuff with the pManager!
};
};
OR
//Design B: singeton approach
class GraphicsManager {
GraphicsManager* getInstance();
};
class Actor {
public:
virtual void renderActor(){
GraphicsManager::getInstance()->SetTexture(0, blah)
VertexBuffer* pVB = GraphicsManager::getInstance()->getVB();
}
};
I guess I'm just wondering/curious what the "hit" is for calling the static
GraphicsManager::getInstance() method...in some cases there's a lot to do (ie. when I'm loading level meshes and/or textures, fonts, etc)..
Is the hit so negligible that I'm worrying about nothing here?
thanks for any advice!
WildFire
11-23-2004, 06:00 AM
Hi wazoo
It makes sense to have the GraphicsManager as a singleton. However, performance wise I can't see how you can lose or gain any on either approach. Optimize your development time, not the runtime.
svero
11-23-2004, 06:23 AM
I'd go singleton but rather than having a single graphics manager at the lowest level I'd have an abstact pure virtual base class that understands and exports interface things to do with blitting and clipping (or rendering if it's 3d) and allow yourself the room to instantiate your single graphics manager in such a way that you can easily swap in and out different ones. I'd also allow that to be dynamic (ie selected at runtime). That gives you the freedom to swap in a software renderer, or opengl, or mac drawing class or whatever. Don't tie the lower end graphics manager to a particular api or platform. I'd say let the factory pull in what it needs dynamically so that you can swap from software to hardware as a config option. You don't necessarily need to implement more than one but if you leave that element of the design flexible then later on you have the option should the need arise for considerations of support or porting.
Tom Cain
11-23-2004, 09:34 AM
I'd go with Design B because it is cleaner to not have to track and pass the parameter.
wazoo
11-23-2004, 10:00 AM
@Tom: Yup, I decided that it is a bit cleaner in terms of not having to
pass it around everywhere.
@svero: Funny you should mention that....I had written some articles about
that (AGES ago) on gamedev.net and I have that abstraction half-completed for a workable game..The problem is, I realized I was spending WAY too much time on the interfaces and debugging d3d/opengl code, then I was focusing on the actual game itself.
I shelved it, and just whipped up a D3D8.1 engine which is a bit dirty, but enough for what I need right now to get things done.
My dream would be to go back and create the library using SDL, then have the 2 rendering interfaces for Direct3D and OpenGL built on top of those...so if the client machine was Windows, it'd use Direct3D and if it was a Mac, OpenGL..
Thanks all! I appreciate the input!
Ronkes
11-23-2004, 10:42 AM
On a side-note, I recommend you let getInstance() return a reference instead of a pointer; less error-prone. And the function needs to be static of course, but you probably know that.
svero
11-23-2004, 08:34 PM
>@svero: Funny you should mention that....I had written some articles about
>that (AGES ago) on gamedev.net and I have that abstraction half-
>completed for a workable game..The problem is, I realized I was spending
>WAY too much time on the interfaces and debugging d3d/opengl code, then
>I was focusing on the actual game itself.
My engine has a lower level abstract interface but it's really tiny. Its not tied to the hardware at all. Very little time was spent designing interfaces (well at least in so much as this particular point is concerned - the engine has evolved over years) so I don't think doing things this way necessarily means one has to spend a lot of time designing.
mahlzeit
11-24-2004, 12:56 AM
If you're using C++, you might just as well get rid of the entire getInstance() method and store the pointer in a global variable. Saves a lot of typing. :)
Mark Fassett
11-24-2004, 01:05 AM
Oh my - the point of the GetInstance() is that, if it's not instantiated, it will get instantiated correctly. Using a global pointer does not ensure this behavior.
Nemesis
11-24-2004, 01:20 AM
With my current project I simply passed the graphic rendering object as an initialisation parameter to every entity. I did this based on the possiblity that I might have multiple rendering contexts. In reality it turned out that I could live with one context so my answer would be that a Singleton will make your life way easier!
mahlzeit
11-24-2004, 02:10 AM
the point of the GetInstance() is that, if it's not instantiated, it will get instantiated correctly
Sure, you could do it that way. You can also explicitly construct the object during the program's initialization phase. Different mechanism, same result. However, I think that gfx->blah() makes easier reading than GraphicsManager::getInstance()->blah(). Of course, it matters how often you'll have to do this. If the code only contains a few such calls, then I would probably use the latter as well.
Wayward
11-24-2004, 03:08 AM
Despite believing singletons are evil (http://c2.com/cgi/wiki?SingletonsAreEvil), I use a monostate (http://c2.com/cgi/wiki?MonostatePattern), which is effectively a singleton (http://c2.com/cgi/wiki?SingletonPattern) (or global) implemented as an all-static class. Using a monostate object is as simple as 'Window::SetTitle("Game")'. There's no tedious 'GetInstance()'. Monostate objects use absolute addresses instead of pointer indirection, so they compile down to the simplest possible code. To avoid a static initialization order fiasco (http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.11) I initialize my monostate classes in my game base class.
Unfortunately, using a singleton (or monostate) breaks the law of Demeter (http://c2.com/cgi/wiki?LawOfDemeter) which I'm quite partial to. The nice thing about 'renderActor(GraphicsManager* pManager)' is that it tells you that the renderActor method uses a GraphicsManager*, while 'renderActor()' hides that fact. The law of Demeter can really show its usefulness when you have a few co-dependent singletons, or you're working with other coders, or you're trying to fathom old code.
So, while I went with the singleton/monostate for my own code believing it to be more convenient (due to fewer method parameters) and more optimal, I've also discovered that hiding what objects a method depends on through behind-the-interface access to global singletons is unclear and dangerous. I think passing an object (and following the Law of Demeter) is much better style.
BigZaphod
11-24-2004, 09:12 AM
Unfortunately, using a singleton breaks the law of Demeter (http://c2.com/cgi/wiki?LawOfDemeter) which I'm quite partial to.
I would say that a monostate also violates the Law of Demeter (although indirectly). It is sort of like knowingly purchasing stolen goods. :)
SuperBug
11-24-2004, 04:20 PM
Actually, singletons introduce a mess, not clarity or elegance. If an object Y needs an object X, than it should explicitly require it (construction time is a good place, or a method parameter). If, instead, you decide that object Y should just use a singleton version of X, then it will require access to the global context and will become unnecessarily tied to something that *is* unique instead of something that should just *appear* unique.
The (singleton) pattern ensures singleness at the global level, for every possible client object. And that's the problem. Every possible client object is now forced to "look" at the global level if it wants to use it. Every possible client object is also forced to use a single instance of that object, even if it doesn't care which instance it is using (mostly the case), only that it does it's job (implements a certain interface).
Actually, to "enforce" the "singleness" property of an object X, you just have to ensure, that that is the only way how a client object Y can see X. You create an instance of X and pass it to Y. Object Y has no way to tell which instance of X it's using, or are there any other. If you use interfaces (and you should), the client object Y also can't instantiate objects of class X by it self. It has to rely on some higher context to provide the instance. For Y, and *just* for Y, X appears as a "singleton": single, initialized and completely managed.
That way, there are no initialization/cleanup complications (as with singletons). Everything is explicit - you create/delete objects in the context that cares for creating/deleting them and knows how to do that (and yes, there has to exist some part of code that does that, otherwise nothing will get initialized nor deleted). There's no need for the unnatural "XYZ::instance()" call and you don't run into unnecessary complications when you want to use commonly used features like inheritance ...
In short, if the constructor or a method demands some object(s), you are explicitly informed what you need to provide, which is not the case if you use singletons - the requirement is implicit, you have to be aware of it, the client code has to be excessively aware of it (know that it's *globaly* single), and you have to provide the needed object in a very obscure and limiting way.
(edited: I messed up with X/Y :rolleyes: - I really shouldn't write posts that late ... although I edit them even later :D)
beenThereDoneThat
11-27-2004, 03:22 PM
Here is a good article on this subject that deals with avoiding singletons when possible, and determining which situations justify their use:
Use Your Singletons Wisely (http://www-106.ibm.com/developerworks/webservices/library/co-single.html)
wazoo
11-29-2004, 05:00 AM
Wow, GEMS of knowledge here I tell ya.
Thanks all, and thanks for the link to that article discussing singletons..I'm popping it open right now to take a read...
Not to throw cold water on the whole "singletons are evil" love-fest, but I can count on approximately zero fingers the number of times I've said "Man this program would be so much better if only this class weren't designed to be a singleton."
--milo
20thCenturyBoy
11-29-2004, 06:28 PM
The GoF have a lot to answer for... :p
20thCB
TamLin
11-29-2004, 07:09 PM
I agree completely with SuperBug: it is far better to make dependencies explicit. If singletons/globals are used because passing arguments to the constructor is too unwieldy, that's a strong sign that the design needs to be adjusted to reduce dependencies. Also, singletons make it more difficult to write automated unit tests.
Incidentally (and at the risk of pointing out the obvious), just because it makes sense to create an object as a singleton (as may well be the case for a GraphicsManager), that doesn't mean other objects have to reference it as a singleton.
crazydonut
12-01-2004, 07:11 AM
Not to throw cold water on the whole "singletons are evil" love-fest, but I can count on approximately zero fingers the number of times I've said "Man this program would be so much better if only this class weren't designed to be a singleton."
--miloWell I have while developing a client/server application using OpenTNL. OpenTNL faciliates some funky global registration scheme that effectively makes sure you cannot create a client and a server with different implementations in the same program (needed because I didn't want client machines to access server code and host their own servers). I got around this by creating a client.dll and a server.dll.
When I make client/server applications I want the ability to wrap up a whole client in a class so I can create many instances like so:
for( int i=0; i < NUMCLIENTS; ++i) client[i] = new Client;
to make it easier to debug hundreds of clients connected to the server. Monostates and singletons destroys this ability. So all third-party libraries breaks my program if they use static globals.
Not really wishing to contribute any more to this 'religeous war', but there is such a thing as over-object-oriented design...
I have been known to use globals (shock-horror!) and even GOTO on occasion (burn the witch!).
I have also seen cases where the function calling overhead due to passing around objects (that should really have been globals to my mind) has caused serious performance issues. One instance springs readily to mind of a complex piece of object-oriented code for PS2 that was almost instruction cache limited due to the over-object-oriented design.
Keep it simple.
vidalsasoon
12-01-2004, 09:12 AM
I agree completely with SuperBug: it is far better to make dependencies explicit. If singletons/globals are used because passing arguments to the constructor is too unwieldy, that's a strong sign that the design needs to be adjusted to reduce dependencies. Also, singletons make it more difficult to write automated unit tests.
Incidentally (and at the risk of pointing out the obvious), just because it makes sense to create an object as a singleton (as may well be the case for a GraphicsManager), that doesn't mean other objects have to reference it as a singleton.
Objects sush as devices are handles on the graphics card <i>globally</i>. So yes, using singletons for this purpose has many advantages. Specially when you want to reset a scene every frame, so you just have to get the singleton and not worry about the device state...
keethrus
12-01-2004, 09:31 AM
I have been known to use globals (shock-horror!) and even GOTO on occasion (burn the witch!).
HA! that's hilarious man. :)
And I totally agree with your "over-object-oriented design" statement.
- Jeremiah
TamLin
12-01-2004, 10:42 PM
I have also seen cases where the function calling overhead due to passing around objects (that should really have been globals to my mind) has caused serious performance issues. One instance springs readily to mind of a complex piece of object-oriented code for PS2 that was almost instruction cache limited due to the over-object-oriented design.
So the design had to be compromised in order to improve performance. That's a perfectly reasonable tradeoff -- so long as the change is driven by profiling the actual program versus premature optimization. That doesn't contradict the general rule to avoid implicit dependencies when possible. It's a lot easier to optimize the hotspots for a program with well-managed dependencies than to improve the design of a program rife with globals and other premature optimizations.
Now, if you're doing low-level, real-time programming (something you obviously know far more about than I do), I understand that the profiling step is sometimes unnecessary if a piece of code obviously violates a crucial constraint.
Nemesis
12-02-2004, 12:36 AM
Just to add my own 0.0000002cents worth re using functions or static GetInstance() singleton methods: if you're working with C++ you can still enjoy the best of both worlds by declaring functions as inline. In most cases this will cause the compiler to inline the function code straight into the calling code, that is, as if there is no function call at all. It doesn't work in call cases (such as virtual functions), but it does improve performance in case of functions in a tight loop.
vBulletin v3.6.0, Copyright ©2000-2009, Jelsoft Enterprises Ltd.