Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Don't forget to read Tuesday's email newsletter for your chance to win a free copy of Construct 2!


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


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
80 replies to this topic

#1 ATC   Members   -  Reputation: 551

Like
3Likes
Like

Posted 07 November 2012 - 06:25 AM

I'm aware of the numerous problems that can arise through the use of the Singleton pattern, which some go as far as calling an anti-pattern... One of the biggest problems is all the references to the global instance/state, for example:

[source lang="csharp"]// inside method body...var obj = Something.Get();obj.TakeAction();obj.Whatever();[/source]

This can really complicate testing and make code that's difficult to maintain...

However, I'm wondering if the Singleton pattern is not ok (or even optimal) for certain circumstances when only one, globally accessible instance of something should exist...

Consider the class I'm currently working on. It is called "NativeMemoryManager", and it is essentially a "safe" API for allocating, manipulating and releasing unmanaged process memory within a .NET application (it uses a 1:1 wrapper I wrote of the CRT memory management API as its back-end -- this is all part of my generalized application & game development framework). NativeMemoryManager inherits "DisposableObject", which is an abstract class that offers a robust implementation of the IDisposable interface; all instances of which are pushed into the "ObjectTable" which is traversed at application shutdown to destroy potential memory leaks...

Only one instance of NativeMemoryManager should ever exist. It's only field is a data table which stores critical information about all memory allocations to prevent memory errors and free any unfreed memory when the application is shutdown. It doesn't need multiple states, and I don't foresee myself ever wanting to replace the singleton instance with some sort of testing instance. Also, if I implement any objects which need to use/consume NativeMemoryManager I will make that object accept the instance in its constructor and store it locally:

[source lang="csharp"]public SomeType( NativeMemoryManager mem ){ this.nativeMem = mem;}[/source]

Then it will use the reference its stores rather than calling NativeMemoryManager.Obtain() over and over...

So considering this particular case and others like it, do you see any problem with using the Singleton pattern? Or is there a better way to approach this?
_______________________________________________________________________________
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!
___________________________________________________________________________________

Sponsor:

#2 mhagain   Crossbones+   -  Reputation: 8140

Like
11Likes
Like

Posted 07 November 2012 - 07:11 AM

My general gut feeling is that if you have one of something then there are always going to be cases where you may eventually need more than one, even if they're not immediately obvious.

Take your memory manager example - right now you only see the need for one, yes. But what if you decide to implement two memory pools, one of which is persistent for the entire application run, the other of which is used to manage memory for the current map you're running? It's incredibly convenient to just be able to throw away all memory for a map in one operation. Then you might decide that a third - for short-lived temp objects (typically a duration of one frame but may be even less) - is in order. And a fourth for temp storage while loading objects. And so on.

These are just examples off the top of my head and I'm not saying that they're all going to apply to your specific case, just illustrating the principle of "having one implies potential need for more than one".

Regarding the singleton pattern itself, I've felt for some time that it is symptomatic of an attempt to shoe-horn an OO design onto something that is not OO in nature more than anything else. Just personal opinion and I'm willing to be wrong on that one, but almost every implementation I've encountered has that hallmark.

If you're talking "objects" you are by definition talking "potentially more than one" so building that potential in from the start can save you lots of hassle later on.

It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.


#3 Hodgman   Moderators   -  Reputation: 31064

Like
13Likes
Like

Posted 07 November 2012 - 07:21 AM

N.B. a singleton is a glorified global (and globals are bad), so any usage (even correct usage) of the pattern is still evil.

for certain circumstances when only one, globally accessible instance of something should exist...

That's the trap -- people confuse "should" with "must".
Singleton is only valid when there "must" only be one instance and it would be invalid for there to possibly be more than one in any possible circumstance, ever... which is quite rare.

Your NativeMemoryManager case is one of those out-of-convenience, "I don't need 2+ right now" cases, where 2 doesn't seem useful, but isn't an error. Therefore using a singleton is possible (many do this), but it's an abuse of the pattern, and the reason it's an anti-pattern. If you write your code in such a way where 2+ instances will work, then the code will be more robust and reusable in the long term, while also still being useful in your short term only-one-instance case.

The most common way of doing this is to write NativeMemoryManager in the non-singleton style, and then create a single global instance of it.
In your case, you're planning on doing the right thing™ of passing the instance to other objects via their constructors, so you don't even need to make a global instance of it anyway...

Edited by Hodgman, 07 November 2012 - 07:23 AM.


#4 swiftcoder   Senior Moderators   -  Reputation: 10238

Like
13Likes
Like

Posted 07 November 2012 - 09:42 AM

Only one instance of NativeMemoryManager should ever exist.

So what happens when your application becomes multi-threaded? Are you going to wrap the NativeMemoryManager in a mutex, and thus serialise memory allocation/deallocation for all threads? Or are you going to allocate one NativeMemoryManager for each thread that needs to use native memory?

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#5 Telastyn   Crossbones+   -  Reputation: 3726

Like
1Likes
Like

Posted 07 November 2012 - 10:23 AM

+1 for Hodgman and swiftcoder answering what I would've said.

Add to that static initialization issues that will come as static fields/members try to use the memory manager to initialize themselves. Certainly uncommon, but a gotcha that will exist for any singleton implementation.

#6 LorenzoGatti   Crossbones+   -  Reputation: 2736

Like
6Likes
Like

Posted 07 November 2012 - 10:36 AM

Consider the class I'm currently working on. It is called "NativeMemoryManager", and it is essentially a "safe" API for allocating, manipulating and releasing unmanaged process memory within a .NET application (it uses a 1:1 wrapper I wrote of the CRT memory management API as its back-end -- this is all part of my generalized application & game development framework). NativeMemoryManager inherits "DisposableObject", which is an abstract class that offers a robust implementation of the IDisposable interface; all instances of which are pushed into the "ObjectTable" which is traversed at application shutdown to destroy potential memory leaks...

Only one instance of NativeMemoryManager should ever exist. It's only field is a data table which stores critical information about all memory allocations to prevent memory errors and free any unfreed memory when the application is shutdown. It doesn't need multiple states, and I don't foresee myself ever wanting to replace the singleton instance with some sort of testing instance. Also, if I implement any objects which need to use/consume NativeMemoryManager I will make that object accept the instance in its constructor and store it locally:

The fact that your motivating example is particularly inappropriate, for already explained reasons, tells a lot about the pitfalls of the Singleton anti-pattern.

You actually want to use a singleton because your NativeMemoryManager has an implementation that expects to be the only instance, but it is actually a rather bad shortcut you shouldn't have taken; reserving different memory areas so that different instances of NativeMemoryManager don't step on each other's toes should be almost trivial, and you should define a suitable Allocator or UnmanagedMemoryAllocator interface allowing not only for test dummies and experimental implementations but also for decorators, composites, and other useful design patterns.
Produci, consuma, crepa

#7 mhagain   Crossbones+   -  Reputation: 8140

Like
1Likes
Like

Posted 07 November 2012 - 06:32 PM

...an implementation that expects to be the only instance...


I love this choice of phrase and it really highlights a key problem so effectively. You've identified a key failing in this design pattern - a huge dependency on factors outside of it's implementation. I'd +10 if I could.

It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.


#8 KaiserJohan   Members   -  Reputation: 1200

Like
1Likes
Like

Posted 08 November 2012 - 08:04 AM

I don't understand how you can completely avoid globals? I completely understand to avoid it as much as absolutely possible, but for instance a logger object. Should you really pass your logger instance to all objects in your game that might need logging? And also say for example you use a framework like GLFW for input, how would you pass on the callback without storing some info globally?

Edited by KaiserJohan, 08 November 2012 - 08:04 AM.


#9 Telastyn   Crossbones+   -  Reputation: 3726

Like
1Likes
Like

Posted 08 November 2012 - 08:30 AM

Should you really pass your logger instance to all objects in your game that might need logging?


Yes.

Making it global is a time saving measure, that you can usually get away with if you're reasonably certain that the downsides (lack of testability, lack of flexibility, dependency issues, threading issues) aren't such a big deal. Even then, it's not what you should do and a singleton should never enter the conversation.

#10 L. Spiro   Crossbones+   -  Reputation: 14010

Like
0Likes
Like

Posted 08 November 2012 - 08:31 AM

I am repeating for the sake of reinforcing what has already been said.

I haven’t actually yet made an entirely non-global project, but I fully acknowledge that not only is such a project possible, it is actually desirable.
I almost wish the C++ language prohibited globals so that I would not have had any ability to have made such design decisions in my engine.

There is really no exception to the rule that any globals you have indicate a design problem.


I am not really answering your question regarding how it is possible to avoid globals, but:
#1: Unless you ask specifics, no one can answer that.
#2: The point is no matter what you think, there is a way. Of course to learn that way you will need to refer back to #1, and ask the appropriate question(s).


L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#11 larspensjo   Members   -  Reputation: 1557

Like
0Likes
Like

Posted 08 November 2012 - 08:45 AM

I think the real issue about singletons are global scope for types, not global scope for instances of these types. If you have a type that is global, which must not be instanced more than once (or zero), then you have a problem. That is, someone else can, by mistake or misunderstanding, instantiate too many of them. That is what the singleton pattern will prevent.

That said, the singleton pattern isn't the only solution, and usually not the best. It is easy to understand and use, and the lazy way out. Another solution would be to not make the type global. And one solution, as mentioned above, is to actually redesign the type so that it allows multiple instantiations anyway.
Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#12 Telastyn   Crossbones+   -  Reputation: 3726

Like
4Likes
Like

Posted 08 November 2012 - 10:06 AM

If you have a type that is global, which must not be instanced more than once (or zero), then you have a problem


There is literally no problem that requires 'you must have only one or things blow up'. There are only broken designs that require such limitation.

#13 Josh Petrie   Moderators   -  Reputation: 3183

Like
3Likes
Like

Posted 08 November 2012 - 10:51 AM

I don't understand how you can completely avoid globals? I completely understand to avoid it as much as absolutely possible, but for instance a logger object. Should you really pass your logger instance to all objects in your game that might need logging? And also say for example you use a framework like GLFW for input, how would you pass on the callback without storing some info globally?

Remember that not everything has to be an object. Turning everything into an object can be very poor object-oriented design, in fact. Logging facilities are something that can be implemented perfectly well without resorting to some kind of "logger object" should you so choose.

But if you choose to build that API as an object or set of objects, then passing those objects to interfaces that require them is a perfectly valid solution and generally more desirable than making those objects global if for no other reason than it makes the dependency on the logging system explicit.

A typical argument for why singletons/globals are good or necessary is along the lines of your initial query: "without global access, I will need to pass this object to every function." The flaw in that argument is that it exists because of a deeper implementation issue: every function is coupled to that object.

Josh Petrie | Game Developer, Undead Labs


#14 davepermen   Members   -  Reputation: 1017

Like
0Likes
Like

Posted 08 November 2012 - 11:19 AM

i've yet to see a place where creating a singleton made sense to me, and "just using one instance" wasn't better.

it might be an issue on how to think about problems, but for me, it never ever made sense.

now i do understand why apis sometimes use the single global-to-you access-point, but even that does not need to be a singleton behind the scenes (it might be once-only for your app, but multiple times existing, say, in the os).
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud


#15 Katie   Members   -  Reputation: 1361

Like
8Likes
Like

Posted 08 November 2012 - 11:24 AM

How about we stop having this argument, because it always ends up with a bunch of perfectionist architects stating categorically no to it and a bunch of practical implementationists saying; the goal here is to write functioning code and anything that makes that happen is OK. And the two are never going to see eye to eye and this topic is boring after about the sixth time around.

#16 swiftcoder   Senior Moderators   -  Reputation: 10238

Like
0Likes
Like

Posted 08 November 2012 - 11:28 AM

The "there can only be one" approach is more than a little weird also from a modelling perspective. Object systems are typically designed to model real-world requirements: can you think of anything in the real world of which only one can ever exist?

Instances are unique (for example, all humans are subtly different from each other), but classes are never representative of only a single object (there can only be one sheep?).

Even things that we think of as solitary instances typically are not - the statue of liberty is treated as unique, but there is another one in France, and half a million small ones in gift shops.

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#17 trasseltass   Members   -  Reputation: 123

Like
0Likes
Like

Posted 08 November 2012 - 11:59 AM

Sometimes, when you do top-down systems engeering using a language like UML to describe the software, singletons pop out when an association between objects is found to have a multiplicity of exactly one (1) in either direction.
https://en.wikipedia...i/Class_diagram

This means that, during run-time, the software itself would NEVER want to destroy or re-allocate this single object instance. Using the singleton pattern for the implementation ensures (through encapsulation of the "instance pointer" or whatever) that it is not possible to destroy/re-allocate the object instance during runtime.

#18 Telastyn   Crossbones+   -  Reputation: 3726

Like
0Likes
Like

Posted 08 November 2012 - 12:15 PM

This means that, during run-time, the software itself would NEVER want to destroy or re-allocate this single object instance.


No, it means that there's a 1 multiplicity of the object (in relation to others). That you're only referencing one object. It sometimes means that you only have one object.

It does not mean that your software cannot (and will never) deal with two.

#19 trasseltass   Members   -  Reputation: 123

Like
0Likes
Like

Posted 08 November 2012 - 12:41 PM

<p><p>


This means that, during run-time, the software itself would NEVER want to destroy or re-allocate this single object instance.


No, it means that there's a 1 multiplicity of the object (in relation to others). That you're only referencing one object. It sometimes means that you only have one object.

It does not mean that your software cannot (and will never) deal with two.


Sorry, I do not follow at all.

"The UML representation of an association is a line with an optional arrowhead indicating the role of the object(s) in the relationship, and an optional notation at each end indicating the multiplicity of instances of that entity (the number of objects that participate in the association)."

If a car has one (1) steering wheel it does not mean that the car has an arbritary number of steering wheels, or that it sometimes has one steering wheel - it always has one steering wheel. If the produced car would have no steering wheels or 5 steering wheels, then it would probably not be the car that we were designing in the first place.

I'm not saying that the singleton pattern is for everyone, just that it solves the design problem of encapsulating a single object instance.

#20 Ravyne   GDNet+   -  Reputation: 7802

Like
2Likes
Like

Posted 08 November 2012 - 12:57 PM

Lots of good points made here against singleton, the way it should be. If the singleton's singleness is mere convenience, you're doing it wrong.

IMO there's only one "rule of thumb" you need to know: Everything that can be implemented without the singleton pattern, should be implemented without the singleton pattern.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS