The Singleton Pattern: To be or not to be [used]?

Started by
79 comments, last by 21st Century Moose 11 years, 5 months ago
Swiftcoder has made a very good point... dealing with multiple threads...

Now that I consider this, perhaps I can redesign the allocation process to create a dictionary of NativeMemoryManager instances indexed by the hash of thread it was created on. That way each thread in the application has its own instance. If the thread is terminated prematurely all resources could be released (EDIT: Unless another thread requires them)...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Advertisement

Now that I consider this, perhaps I can redesign the allocation process to create a dictionary of NativeMemoryManager instances indexed by the hash of thread it was created on. That way each thread in the application has its own instance. If the thread is terminated prematurely all resources could be released...

And now you have designed a need for cross-thread hash container that requires explicit synchronisation... All to hang on to the Highlander rule (aka "there can only be one").

Designing additional layers of abstractions on top of flawed design patterns is not a solution wacko.png

Edit: Also, what you are describing already exists. See Thread Local Storage.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I'm aware of that. But how would you suggest approaching the design then?
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
What's wrong with the obvious solution: don't make it a singleton, don't use global state and let the client decide if it wants to synchronize access to a single allocator or use one per thread without synchronization?

A global is even quicker and easier to implement, and is arguably less objectionable.


A global is at least honest about what it is. You know what you're dealing with as soon as you see one. A singleton tries to dress it up in pretty syntactic sugar, which can make it seem more acceptable to some.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


I don't have any more time for this, but it's always nice to discuss Singletons. Lots of strong opinions. smile.png

Just so you know, leaving the conversation before being convinced of the opposite side doesn't mean you won tongue.png

TBH, I never use singletons for game development. I have however seen it used for embedded software projects that are strongly linked to using UML.[/quote]

Embedded systems are one of few domains where you might actually encounter a legitimate singleton, but it has nothing to do with UML. UML can be a useful tool surely enough, but it seems more often than not to be a sign of government contractees or over-engineered java contraptionists, there's certainly no correlation between those that use UML and those who can actually engineer code.

The average UML diagram is about as far removed from actual code as a 3rd grader's drawing of a car is removed from the production line.


Avoiding globals and singletons can lead to a lot of unnecessary upfront design work. "You might need more than one down the road" is the same rhetoric that justifies layers and layers of abstraction. "But I need to make it extensible for the future!" You don't know what the requirements will be in the future. Don't waste time over-designing something that will never be used.

Except that singletons usually *are* extra work. You jump through hoops to ensure their "singleness" when a simple global would do the same job. Neither is a great long term solution, but if you can't be persuaded to put in the work of actually designing your code properly, I fail to see what good wrapping your global state in a single-serve, tin-foil package buys you.

throw table_exception("(? ???)? ? ???");

I'm probably gonna get creamed for saying this, but I usually use namespaces for systems that I only want one of. I've never felt the need to make a singleton. It seems to me like a complicated way of doing what a namespace does simply. TBH when I used to read about the singleton in books I would feel bad, like I was failing to understand something, because I just couldn't see why the hell anyone would want to do something like that.

That being said, I am fully aware that using a namespace to encapsulate some kind of system means that I can only have one, and I make that decision on a program-by-program basis. If it's even remotely possible that I may want more than one of something then I'll make it a class. If it turns out later that I was wrong then I can either change the class into a namespace (To un-clutter the code - I hate passing some object to every gorram thing in the program.) or just leave it as is.
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
What I get from this is that the right way of doing it is to pass objects to each function needing it, avoiding globals/singletons completly. But ain't there problems with that as well? Lets say I have an Input class and a Graphics class. It seems convenient that I pass the Input object to Update() and the Graphics object to Draw(), like this:

[source lang="cpp"]void Game::Update(float dt, Input* pInput)
{

}

void Game::Draw(Graphics* pGraphics)
{

}[/source]

In Game I might have a World class which also have Update() and Draw() methods. And in World there is a Player, but the Player class should be able to call a function in Graphics when a key is pressed. I can't do that in either Update() or Draw() since they only are passed one of Input and Graphics. In this case I find it really useful for one of them to global since I don't have to bother about this issue. I can see two options to solve this without the use of globals.

  1. Change Game::Update() and World::Update() to take a Graphics* parameter.
  2. Add a Graphics* mGraphics member variable to each class that needs it, and set it with SetGraphics(Graphics* pGraphics) on initialization (what if I forget to set it?)

Maybe there are more options, what do you recommend doing?
Has anyone any thoughts on the enum Singleton in Java?

It avoids some of the problems that people have been bringing up here with the Singleton pattern - An enum in Java is an honest global, initialization is thread safe.

I've recently use this method to implement states for a finite state machine. The state doesn't store any instance data, just operates on the object that is passed to the state methods. I could do it without singletons, but it made sense to me to have one and only instance of the state logic. There is also very little extra syntax (less than even writing as a ordinary class). It seems to work really well.

[source lang="java"]public enum Singleton {
INSTANCE;

public void methodA() {
}
}[/source]


Just throwing this out there. Not saying I'm right either. I'd probably avoid Singletons like the plague if using C++.

I'm probably gonna get creamed for saying this, but I usually use namespaces for systems that I only want one of.

I hope you won't get, or people will eventually get afraid asking things.

I am not sure I understand how the use of namespaces limits the number of instances to a maximum of 1. The purpose of the singleton pattern is to make it impossible to instantiate more than one object from global type. The purpose is also to send a message to other people messing with the source code that it is designed in such a way that it will stop working if there is more than one instance. If you declare the type in a namespace, it doesn't stop anyone from using it incorrectly.
What I get from this is that the right way of doing it is to pass objects to each function needing it, avoiding globals/singletons completly

The singleton pattern and global variables are two different problems (although they tend to be misused in interesting combinations). The singleton pattern is a way to protect global types from misuse, which is independent of the problem on how to use or not to use global parameters. Apart from that, I would say your design proposals are fine (depending on situation).
[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

This topic is closed to new replies.

Advertisement