Jump to content
  • Advertisement
Sign in to follow this  
_Sigma

When to use and not to use singletons

This topic is 4040 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am starting to get worried that I may be overusing singletons in my game. What I'm looking for is a little guidance into when to use singletons. I have currently been of the opinion that I should use singletons when I want to enforce the creation of only one object. I am using singletons for:
  • Logging system
  • Script engine
  • Input
  • Renderer
Is this overkill? Obviously there is a little bit (am I right?) of overhead on the ::GetInstance() function call, but nothing that should be a showstopper - correct? Now I could make the input system a member of the kernel...but there should be only one input. I am also the only person who is developing this as well as (most likely)the only person to use the code later on. Modularity is key for my desgin, as I want to be able to redo the engine in version 2 and 3 to support 3D + multi threading. So I need to be looking towards the future (am currently doing 2D - no threading) Any suggestions? Comments? Cheers [Edited by - _Sigma on May 23, 2007 11:46:09 AM]

Share this post


Link to post
Share on other sites
Advertisement
There's no reason any of the subsystems you listed should be singletons.

Quote:

I have currently been of the opinion that I should use singletons when I want to enforce the creation of only one object.

Singletons are for when there must be only one instance and you must provide global access to that instance. None of the subsystems you listed meet either criteria (singletons are not about protecting yourself from yourself or other programmers, which is what you're attempting to do -- "I uwant to enforce the creation of only one object" is not "there must be one only one.").

Quote:

Is this overkill? Obviously there is a little bit (am I right?) of overhead on the ::GetInstance() function call, but nothing that should be a showstopper - correct?

It's not the overhead of the implementation detail that makes singletons bad (in many implementations the overhead can be compiled out entirely).

Quote:

I am also the only person who is developing this as well as (most likely)the only person to use the code later on.

This is not an excuse to write poor code or develop poor designs, unless you are a poor programmer. Don't be a poor programmer.

Quote:

Modularity is key for my design.

Singletons reduce modularity and extensibility.

Okay. Now, I've pretty much come to the conclusion in my years of experience that there is no need, ever, for a singleton. At best I will admit to there being a very-very-very-very-small-but-non-zero percentage of situations that actually warrant a singleton. Here's why:

Consider aspect one of the singleton: "There must be exactly one instance." The "must" is critical, here. Not "we want there to be only one" but "there must be only one." This situation rarely occurs at all, and never occurs in game development (it's usually restricted to very low-level embedded systems or when you're building an interface to some particularly wonky hardware, and in those cases you tend to be using lower-level methodologies for your code anyway). This trait is often twisted from "must be" to "want to be" by programmers, however; this is bad, because this is writing code in an attempt to protect the programmer from himself or other programmers. A truly determined programmer will subvert your protections trivially regardless of what you do; you're just breaking your design and making work more difficult for those programmers who want to use your interface correctly in a vain attempt to protect yourself from the edge case: the programmer will be knowingly doing something stupid.

This is further underscored by the fact that you, in this particular case, claim to be the only developer. You know the proper way to use the interface is (for now) to create only one. So create only one. Don't waste time breaking your design and jumping through hoops to restrict yourself with a restriction you don't really need.

The second issue: "Global access must be provided." It is again rare that global access is to a subsystem is needed in a well-designed API ecosystem. The reason people often perceive global access as required is because their overall design is broken. The Input subsystem is needed only by those parts of the game that process input and translate that input into commands to other underlying subsystems (game logic, rendering, physics, whatever). Similarly for other subsystems. Yes, there will be a common "connection point" where all or most subsystems exist at the same "visibility level" (this is typically in a "game" object somewhere that deals with code and methods specific to the game or project), but global access is not required.

Even logging can be done without the aid of a globally-accessible singleton object, if you think hard enough about it. And none of the other subsystems required singletons, either. Since they don't require them, why limit yourself, especially if you're at all concerned about future directions? Because singletons are such a crutch, they creep all over the codebase, allowing you to take the easy hacky solution (often without even thinking), and making refactoring and maintainability for future modifications a hellish thing.

Unfortunately, since singletons are often a cure-for-a-symptom rather than a cure-for-the-problem (a bad design), it's impossible to give a straight, reusable solution to removing singletons from your code. It depends on the designs in question -- if you provide some more information about the overall architecture of your project and how things intercommunicate, I could provide some specific suggestions.

It boils down to considering lines of communication and interdependency, and removing as many dependency couplings as possible, bubbling them up to higher level subsytems (like the "game" object).

[Edited by - jpetrie on May 23, 2007 11:53:49 AM]

Share this post


Link to post
Share on other sites
Personally, I don't think you should ever use singletons. I really mean that -- there are legitimate uses of goto once in a while, but singletons?

Let us ignore the design errors that would lead you to use a global single instance class. Suppose that for some reason, you want to make those mistakes and nobody is going to stop you. Even then, why would you use a singleton? What exactly is gained over free-floating data (presumably in a namespace) or static data in a dummy class? The only answer I've seen that I'm willing to buy is that the singleton protects volatile/synchronized data, so you need to force a single point of access (this would be the singleton getter).

And if you can tell me with a straight face that you think a design that requires a singly instanced, globally accessible, volatile/synchronized class is not fundamentally broken, then I'm simply going to walk away from the debate.

Share this post


Link to post
Share on other sites
haha. Well I guess that answers that! Looks like I need a redesign...:(

edit:
Quote:
...and you must provide global access to that instance.

Actually, the logging system is the only one I believe falls under that, as it is used in almost all the other classes.

[Edited by - _Sigma on May 23, 2007 11:09:43 AM]

Share this post


Link to post
Share on other sites
Disclaimer: This is all my opinion you may disagree.

You shouldnt use singletons just because you want to enforce the creation of only a single instance but instead when
a) There is no possibility that anyone could ever require more then one instance, i.e. there is a big logical flaw to being able to create more then one instance or
b) The effort required to support the creation of more then one instance (implementation wise) is far greater then the potential pitfalls.

So following a and b above:

Kernel:

Cant really comment because I dont know anything about your design but if its possible to run multiple copies of the game then this shouldnt be a singleton, in particular consider weather supporting multiplayer would require multiple instances of this.

Logging system:

This shouldnt be a singleton because you may require more then one logger and its not hard to concieve of such situations, for example, you want to log graphics related and network related things to different locations.

Script engine:

I think this may validly be a singleton depending on the design, however, I think that this is more due to the difficulty of supporting multiple scripting engines if you havnt designed for it from the start rather then from logically requiring only a single scripting engine, for example if you where to script GUI objects and game characters then you may well want to use more then one scrippting engine so you can dispose of the resources that the scripting engine uses for the GUI once the actual game starts. Basically if your just starting I dont think theres any reason for this to be a singletonn, however, its also one of the ones with the least compelling reasons to change if your working with existing code.

Input:

Input shouldnt be a singleton because multiplayer would require more then one location for recieving input (weather this is from a network or keyboard/mouse). In addition supporting input from multiple locations should require very little effort in comparison to only supporting a single input.

Renderer:

I dont think a renderer should be a singleton because you may require (want to use) multiple renderers to give the players views of different areas of the world particularly in a multi monitor scenario.

Quote:
Is this overkill? Obviously there is a little bit (am I right?) of overhead on the ::GetInstance() function call, but nothing that should be a showstopper - correct?


Depending on the implementation of GetInstance there can be from zero to a very tiny bit of overhead but yes theres nothing that should be a showstopper.

Edit: To slow, jpetrie say's it much better.

Share this post


Link to post
Share on other sites
Read my edits and give me some more detail on your current layout, and I can give you some suggestions.

Share this post


Link to post
Share on other sites
Quote:
Original post by Promit
And if you can tell me with a straight face that you think a design that requires a singly instanced, globally accessible, volatile/synchronized class is not fundamentally broken, then I'm simply going to walk away from the debate.


I have a design that requires a singly instanced, globally accessible, violatile/synchronized class, which is not fundamentally broken.











It's a design that's designed to serve as an example of what not to do, and it does a fine job of that.

Share this post


Link to post
Share on other sites
I used to be a fan of the singleton logger. Then I started using NLog and I realized there is a much better way.

Share this post


Link to post
Share on other sites
Thanks for the long replies - answers a lot of questions!

Quick note - kernel is not actually a singleton sorry!

This is my current design (now without singletons - only change is that they used to be singletons :S - Please don't hurt me!!! O_o lol). The following diagram does not show inheritence, rather just how
objects are in relation to one another. ie, a TScript instance is a member of TKernel




TKernel TLog (global singleton)
|
+----------+----------+
| | |
TScript TInput TRender



The kernel holds a main() that is called into from WinMain. Here we make the calls to handle the
windows messages before handling our own.

Once done, there is a ScriptMain() function in the script that does all the high level work, such as
adding things to the render queue(ie: Draw(player) ), moving monsters, etc. Other funtions in the script can be registered
to be called on input, so


void OnKeyLeftDown()
{
player.moveLeft(10); //10pixels left
}

and input would then, upon detecting a key down, check if there is a function associated with this action, and if so, call it.

On return to the kernel.main() (ie, exiting the script main), we do all the grunt work , such as
the actual rendering, the AI, pathfinding, etc.

On the next iteration, call back into the script main function, and repeat.

Is this enough information? I'm currently still fleshing out some design ideas, so not everything has been written down yet!

Thinking this through, everyone's advice on not using all those singleton's seems to make a lot of sense. I think I was caught up
playing with something new!

At the moment, TKernel really is the top object, and everything below it doesn't need to know about the others.

THe only interface a user gets is what ends up being exposed to the scripting language (Angle Script in this case)

Cheers
Chris

Share this post


Link to post
Share on other sites
Quote:
Script engine:

I think this may validly be a singleton depending on the design, however, I think that this is more due to the difficulty of supporting multiple scripting engines if you haven't designed for it from the start rather then from logically requiring only a single scripting engine, for example if you where to script GUI objects and game characters then you may well want to use more then one scrippting engine so you can dispose of the resources that the scripting engine uses for the GUI once the actual game starts. Basically if your just starting I dont think theres any reason for this to be a singletonn, however, its also one of the ones with the least compelling reasons to change if your working with existing code.


Currently only one script engine is supported, and I think actually warranted.

I'm actually at a bit of a undecided point regarding how I want to full implement this. I am just implementing the basic parts to get a feel for what
can be easily done with AS, before full sorting out what I want to do.

Eventually, I would like each level to be fully scripted with events that are triggered depending on global events (in the game world).
Since you can have AS compile scripts into different namespaces, I originally thought to separate AI from Input. But then it became difficult
to keep track of which namespace (module) each function was in, so I just now have all scripts compiled into the same module.

Currently, I envision this being done like thus:

- each .map file contains
= all the art assets for the map
= tile maps
= script file(s)
= etc...

The tile map would then have references to a script function if we wanted to process a scripted event.

As well, AS allows for using the include directive, so if you want to reuse code this is easy, and allows for communication among script files.

So if (and this is really down the line) I wanted to script GUIs, I think I would just compile into a separate module instead of a new
engine...

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!