Jump to content

  • Log In with Google      Sign In   
  • Create Account


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

#21 Telastyn   Crossbones+   -  Reputation: 3718

Like
3Likes
Like

Posted 08 November 2012 - 01:01 PM

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.


Exactly. It does not mean there is only one steering wheel in the entire universe, which is what a singleton enforces.

Sponsor:

#22 trasseltass   Members   -  Reputation: 123

Like
0Likes
Like

Posted 08 November 2012 - 01:12 PM


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.


Exactly. It does not mean there is only one steering wheel in the entire universe, which is what a singleton enforces.


If you are designing a universe I could see how that would be true. If your "system of interest" is a bit smaller, for example a car, it perhaps only exists one steering wheel.

#23 swiftcoder   Senior Moderators   -  Reputation: 9612

Like
0Likes
Like

Posted 08 November 2012 - 01:17 PM

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.

But how many cars are there? For there to only ever be one steering wheel, there would also have to only ever be one car.

Your UML scenario indicates that there may be many-to-1 relations between objects. It in no way implies that there may ever be absolute many-to-1 relations.

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

The "design problem of encapsulating a single object instance" is exactly that, a design problem.

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#24 larspensjo   Members   -  Reputation: 1526

Like
0Likes
Like

Posted 08 November 2012 - 01:19 PM

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.


I can think of four reasons where this may not hold true. I don't say that the following 4 cases always are true, just that there are situations where they are true.
  • Doing an implementation for a thing that there must be only one of in such a way that the design allows for multiple future extensions takes more effort. Usually you have a limited budget, and the singleton pattern is very quick and easy to implement. The designer of the code maybe just isn't experienced enough, and as a project leader you sometimes have to accept something that works, even if it is not optimal.
  • Testing the general purpose variant of the design may take more effort. (Yes, I know, using global instances of a singleton may also add to the testing effort, but the discussion about singletons is not the misuse of global objects).
  • A generalized class can cost more CPU time. I am not saying it will, or that it really matters, just that it may be the case.
  • The factory pattern is based on the singleton pattern. That is, it usually uses classes with a private constructor to force clients to use the factory methods.

Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#25 trasseltass   Members   -  Reputation: 123

Like
0Likes
Like

Posted 08 November 2012 - 01:28 PM

> But how many cars are there? For there to only ever be one steering wheel, there would also have to only ever be one car.

Sorry, I forgot to mention that the system of interest in this example is the car. We're designing a single car and eventually the goal is to deliver instances of this car, maybe in larger numbers. We're not designing and delivering a composite of multiple cars. Posted Image

Edited by trasseltass, 08 November 2012 - 01:28 PM.


#26 swiftcoder   Senior Moderators   -  Reputation: 9612

Like
2Likes
Like

Posted 08 November 2012 - 01:37 PM

Doing an implementation for a thing that there must be only one of in such a way that the design allows for multiple future extensions takes more effort. Usually you have a limited budget, and the singleton pattern is very quick and easy to implement.

A global is even quicker and easier to implement, and is arguably less objectionable. Passing in a pointer to an instance is barely more complicated.

Testing the general purpose variant of the design may take more effort. (Yes, I know, using global instances of a singleton may also add to the testing effort, but the discussion about singletons is not the misuse of global objects).

Singletons (and globals in general) are the anathema of testing. Anytime a class calls to a global, they escape your test harness., and you are screwed.

A generalized class can cost more CPU time. I am not saying it will, or that it really matters, just that it may be the case.

Artificially restricting the use-cases of a class to a single instance is the path that requires additional effort here. Not enforcing those constraints is free.

The factory pattern is based on the singleton pattern. That is, it usually uses classes with a private constructor to force clients to use the factory methods.

No, just, no. If your factories are singletons, then you are doing that wrong too.

What happens when you need to dynamically replace a factory at runtime, or any of a dozen more trivial use-cases?

We're not designing and delivering a composite of multiple cars.

And when you need to instantiate multiple cars in your test harness, what then? Try telling an automotive engineer that he is designing a car of which only a single will ever be built, and no prototypes either...

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#27 trasseltass   Members   -  Reputation: 123

Like
0Likes
Like

Posted 08 November 2012 - 01:50 PM

We're not designing and delivering a composite of multiple cars.

And when you need to instantiate multiple cars in your test harness, what then? Try telling an automotive engineer that he is designing a car of which only a single will ever be built, and no prototypes either...

If your test subject for a black box test is the entire system, e.g. the car, in what situation would you need to create multiple instances of it? Sounds like something that is dependant on the design of your test harness / test environment. I think you're confusing design with production - it should be perfectly fine to design a single system and then produce multiple instances of it.

#28 trasseltass   Members   -  Reputation: 123

Like
0Likes
Like

Posted 08 November 2012 - 01:53 PM

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

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.

#29 et1337   Members   -  Reputation: 1365

Like
2Likes
Like

Posted 08 November 2012 - 02:01 PM

I am still definitely on the side of "singletons are evil" but less so than I used to be.


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.

Another thing I've discovered is that global functions and global state are two entirely different things. I used to try to minimize dependencies by not using global functions, but really those are totally fine. It's global state that's more dangerous.

#30 swiftcoder   Senior Moderators   -  Reputation: 9612

Like
1Likes
Like

Posted 08 November 2012 - 02:29 PM

It's global state that's more dangerous.

One could argue that it's the 'state' that is dangerous, rather than the 'global' part of it.

Stateless systems are far easier to test and prove correctness of, after all.

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#31 ATC   Members   -  Reputation: 551

Like
0Likes
Like

Posted 08 November 2012 - 03:28 PM

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)...

Edited by ATC, 08 November 2012 - 03:28 PM.

_______________________________________________________________________________
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!
___________________________________________________________________________________

#32 swiftcoder   Senior Moderators   -  Reputation: 9612

Like
0Likes
Like

Posted 08 November 2012 - 03:31 PM

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 Posted Image

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

Edited by swiftcoder, 08 November 2012 - 03:38 PM.

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#33 ATC   Members   -  Reputation: 551

Like
0Likes
Like

Posted 08 November 2012 - 03:41 PM

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!
___________________________________________________________________________________

#34 SiCrane   Moderators   -  Reputation: 9392

Like
0Likes
Like

Posted 08 November 2012 - 08:13 PM

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?

#35 mhagain   Crossbones+   -  Reputation: 7436

Like
1Likes
Like

Posted 08 November 2012 - 08:23 PM

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.

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.


#36 Ravyne   Crossbones+   -  Reputation: 6769

Like
2Likes
Like

Posted 09 November 2012 - 12:40 AM

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

Just so you know, leaving the conversation before being convinced of the opposite side doesn't mean you won Posted Image

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.


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.

#37 Khatharr   Crossbones+   -  Reputation: 2821

Like
0Likes
Like

Posted 09 November 2012 - 03:35 AM

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.

Edited by Khatharr, 09 November 2012 - 03:45 AM.

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.

#38 simpler   Members   -  Reputation: 912

Like
0Likes
Like

Posted 09 November 2012 - 05:47 AM

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.
  • Change Game::Update() and World::Update() to take a Graphics* parameter.
  • 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?

Edited by simpler, 09 November 2012 - 05:48 AM.


#39 PurpleAmethyst   Members   -  Reputation: 305

Like
0Likes
Like

Posted 09 November 2012 - 06:34 AM

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++.

#40 larspensjo   Members   -  Reputation: 1526

Like
2Likes
Like

Posted 09 November 2012 - 06:43 AM

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).
Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/




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