Am I using singleton when I shouldn't?

Started by
18 comments, last by L. Spiro 12 years, 5 months ago

I do however like it because I can create something I need everywhere with functionality attached to it.


This is precisely why singletons are bad.

If you get in the habit of "oh, hey, I'll just snag a little of this and throw it over there," you're screwing yourself in the long term. Your code will, as it increases in size, become difficult to reason about. It will be hard to maintain. And you will get weird bugs that are extremely frustrating, because there will be a tangled mess of intertwined responsibilities spread across half your code base.

There are fabulously few things you "need everywhere" in any program. Most of them do not deserve to be objects, let alone singletons; and the few that do need to be objects rarely need to be singletons anyways because engineering your code to assume there is only "one of these" makes it needlessly brittle. Someday you'll find a reason to need two, and then you're screwed.

Don't think of this so much as "doing X will lead to badness" but more of "not doing X will enable more goodness." Not using singletons will allow you to design code that is loosely coupled - things that shouldn't be related won't be. This is great because it means localization of concerns. You no longer have to look at your physics code to figure out why the high scores are showing up wrong. You no longer have to worry about things like your menus accidentally deleting themselves.


The key to good software design is minimalism. One class should have one and only one responsibility, for instance. One concept should be mapped to as few implementation details as possible. One implementation detail should affect as few high-level concepts as possible. The more simplistic and contained your code is, the easier it is to think about and reuse.


Consider two alternatives.

In the first world, you manage game states using a singleton. Every time you want to update the current game state, you ask the singleton to do it for you. Game state management is spread across most of your code.

In the second world, you manage game states in a centralized location. No implementation of any one game state says where to go next, or how to do it; instead, you contain the entire state machine to a single function in a single file.


Maybe you find the first method simpler now, but that's just because you've dug yourself a rut and need to pop out of it. The second method is probably actually easier to write.

But now you have to add a new game state, or maybe make the high score screen pop up after you die but before you go back to the main menu.

Which code is going to be easier to work on?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Advertisement

[quote name='Moehammered' timestamp='1319523782' post='4876639']But just for discussion sake, I seem to find singleton convenient, is there anything wrong with this?
The first 4 lines of code that you posted create 4 global variables:
Globals/singletons are ok on small projects. On large projects or reusable software, they make it impossible to reason about the code-base. At work (as an engine programmer), I would be dragged into an office and be given a stern talking to if I used these kinds of patterns, because of all the pain and suffering they cause.
[/quote]

Thank you for that, made me feel less awful :P

But than 'from your experience' what would you do? Psuedo code is fine I can understand it well or even logic will give me perfect insight :)
ApochPiQ thank you for generalizing it for me. I see how you've explained it and I myself understand singleton is the exact opposite of OOP so I see now that I have been very 'lazy' to look for a more effective solution. And now that I think about it I can see a use case where my code would fail. If I were to have 'split screen' gameplay.... this would fail. So yes I want to re-design it tonight. Thanks for the help, any implementation suggestions? :)

(Side Note: Anyone know where I can go to get info on how to integrate scripting into an engine? like say write a C++ game engine, and have it accept Ruby script for example)
I see no reason to use globals at all, and in fact a lot of the system wouldn’t even work as globals.

Let’s say you have a CStateBase class. I guess this is your CState class.
The CStateManager maintains the current game state, which must be derived from CStateBase.

The engine has no idea what classes you are going to create over CStateBase when it is time to actually make a game on top of your engine.
And of course it is not acceptable to modify the engine for each new game, adding the new states needed for that game etc.

So you need a CStateFactory.
The engine can provide a small set of default states (mine has 2: One prints a message saying the engine is running and the other causes the engine to shut down).

The class has only one function: GetState(). The function accepts an integer that identifies the state you want to have created and returned.
You can just make an enumeration for all of the states in your game and inside CMyGameStateFactory::GetState() you just use a switch case to create the proper state class given the enumerated value.

Thus if your state factory is global you can’t override it and you can’t make custom states.


And what brings these functions together?
The CGame class is intended to bring together the major sub-systems. It holds a pointer to a CStateMachine and a CStateFactory, and a pointer to the CGame class is passed to the Initialize(), Tick(), Draw(), and Destroy() functions in CStateBase, so states have access to any of the major systems they need as well as the game’s data.

When you create a game you make your own game class (CMyGame : public CGame) and set its state factory.
mgMyGame.SetStateFactory( new CMyStateFactory() ); // It doesn't leak.

With this system in place, you can go from any state to any other just by calling CGame::SetNextState(). CGame will go to that state on the next cycle (this is important, because the only thing that can change to a new state is the current state (it is the only thing that can do anything in your game) and if you delete it, control will return to its Tick() function but its this pointer will be invalid), asking for the new state pointer from your custom state factory and handing it off to the state machine


This describes a simple way to organize your game into modular states and has no logical use of globals of any kind.
Even the CMyGame class would just be created on the stack.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


But just for discussion sake, I seem to find singleton convenient, is there anything wrong with this? What are the pitfalls I can expect and if so would they be very bad to the point they would cause negligence in my code? Of course I don't like to overuse them because well... if anyone's used Ogre3D IT IS HELL TO USE THEIR SINGLETON DESIGN!! :/

I do however like it because I can create something I need everywhere with functionality attached to it. I'm a bit put down cause I feel I'm somewhat making myself a bad programmer by using this pattern now... :(


I think the biggest problem with singletons is when people use them too much, with no real reason, without really thinking about it, and even when they are just starting with OOP. Some people simply think using singletons is cool.
You are neither of this, because otherwise you wouldn't have created this thread.

My opinion - if it's your own project, if you are not in a team with other programmers who don't like singletons, you can use them if you want. IMHO, if you know very well what you are doing, you can do it. And knowing what you are doing includes also knowing other ways how to do it (without singletons) and being able to implement them.
If you prefer a global variable for something what you need access to in a lot of modules, then why not? And if you like a singleton more than a "simple" global variable, then why not?

[quote name='ApochPiQ' timestamp='1319521260' post='4876623']
Of course, in the latter case, the question arises: why is your menu of all things deleting game states?


State *currentState = new Menu();

void Menu::update()
{
if(Play button is pressed)
{
currentState = new Game(); // This would leave garbage behind as the pointer for currentState has changed has it not?
/*OR*/
delete currentState;
currentState = new Game();
//But now since this is a function call inside the Menu class, and the currentState has been deleted,
//than the menu would not be able to run this piece of code because update is a function inside the menu class.
}
}

[/quote]

Ugh. Please, no. That confuses (fails to properly segregate) responsibility.

See how it looks when you separate concerns - the state determines what comes next, and the code that logically contains the "current state" value actually manages that value:


State* Menu::update() {
if (Play button is pressed) {
return new Game();
}
return this;
}

// And then for example you could do something like:
int main() {
State* current = new Menu();
while (current) {
State* next = current->update();
if (next != current) { delete current; }
current = next;
}
}


Notice how you no longer need a global variable at all. Also you get a way to signal program exit for free: just return null.

You could also instead use some other policy for managing the states: it might make sense for your application to cache game states, or re-use them, or store them in a stack, or whatever. The important thing is to make sure each instance is deleted exactly once. The easiest way to handle this is to not try to make the states themselves responsible for it.

But please don't use raw pointers for this; use a smart pointer (e.g. boost::shared_ptr).
Also, what happens if you want a Paused state? You certainly don't want to delete the old state, else you would have no game to resume. This is one reason I like a stack to manage my game state.

Also, what happens if you want a Paused state? You certainly don't want to delete the old state, else you would have no game to resume. This is one reason I like a stack to manage my game state.


A stack is certainly one way of doing it; I personally lean towards confining all state transition logic to a single function or object someplace, as I outlined above, but that's just me ;-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

IMX a stack is overkill. A Paused state can just hold a pointer to the state that's been paused (and if you're using reference-counted smart pointers, or a GC language, then that keeps the instance around).
I agree that a stack is overkill when all you want to do is pause the game. I do allow a stack of states, but it is for system states such as when a Nintendo Wii pops up some kind of warning message that interrupts your game, such as having the disk taken out.
To me this is a more sensible use of a stack of game states.

Pausing is done by simply not advancing the virtual time accumulators. Any object that uses the virtual time instead of the actual time will simply stop moving. That simple.
This way menus and graphical effects etc., can continue bouncing and flashing while the game is paused while the 3D game scene has stopped. If you want to show a message or darken the screen while paused, simply check the CGame::Paused() function and do so.

When a state is pushed over the current state via a notification from the console, all bottom states are inherently paused (a virtual function on the CStateBase class can be overloaded to prevent a state from being paused, which may be necessary for online games).


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement