Skip to main content
GameDev.net gamedev.net
🔒 Locked

Pros and cons of singleton classes

Started by EmpireProductions Jan 29, 2010 at 5:20 AM 61 replies 20.9k views
Original Post
EmpireProductions
EmpireProductions
I have a bunch of manager classes and now my Terrain Engine that all need to be able to interact. Alot of them I have made into singleton classes so I can just call their methods where I need to. However with designing my terrain engine I am trying to make it as fast loading as possible since it will be loading and unloading content in the background as the player is moving around. I was just wondering if it was a good idea to make these classes singletons or if theres another method I should be using. Also is there any performance decrease with singletons or is it just as fast as if it wasn't singleton?
In Development:Rise of Heros: MORPG - http:www.riseofheroesmmo.com
Scarabus2
Scarabus2
I use singletons too, but I try to keep them to a minimum. For instance, I wouldn't want anything that has to do with rendering to be put in a singleton because there may (and have) come a point where I want to create more than one instance of my graphics engine in the same app.

Also, in b4 flame war.
visualnovelty.com - Novelty - Visual novel maker
EmpireProductions
EmpireProductions
Ok thanks if the only con to using them is that you can only have 1 instance of it then that won't be a problem for the terrain engine. Theres no way that I would want to have more then 1 instance of the terrain engine going at one time. As it is the terrain engine will have to be using 2 or 3 other threads from the main game thread just so it can do what it needs to do.
In Development:Rise of Heros: MORPG - http:www.riseofheroesmmo.com
Brother Bob
Brother Bob
Quote:
Original post by EmpireProductions
Theres no way that I would want to have more then 1 instance of the terrain engine going at one time.

You will say that until the point where you actually need more than once instance, at which point you have locked yourself into a design choice that prevents you from doing it. Why introduce an artificial limitation that has no purpose now, and may limit you later?
Zakwayda
Zakwayda
Quote:
Theres no way that I would want to have more then 1 instance of the terrain engine going at one time.
This is how the decision to use a singleton usually begins: With, "There's no way I would want to have more than one sound manager/texture cache/logger/entity manager/game world/physics environment/scripting state/message dispatcher/collision context/etc., etc.".

Like Brother Bob said though, situations where you actually do want to have more than one can easily arise, so why limit yourself artificially? If the main (or only) reason you're using a singleton is because there's 'no way you'd ever want more than one', consider just using a global object instead. (You mentioned multithreading, so I realize there may be more going on here than is immediately evident.)
davetyler
davetyler
It makes much more sense to think about it in terms of

"I CAN never have more than one of this class"

If that is true then it should be a singleton. It is an artificial limitation on your code in the same way that making member variables private and having get/set routines is an artificial limitation. Artificial limitations aren't necessarily bad.

One example that I often use singletons for is database access. I have a program parsing database tables into other tables and it has a singleton DatabaseAccessLayer class that can do all of the getDataSet etc routines.

So the question for you is:

"Is it possible to have more than terrain manager"

NOT

"Do I currently need more than one terrain manager"
snake5
snake5
There might be some exceptions about singletons - when the class is not designed as a usual singleton but only a global instance or when the singleton is designed to be extensible in a way that you will never need another instance of the class (like a logger with ability to add custom output devices).
Otherwise, it makes no sense to use the default kind of a singleton (the instance of the class is in the class itself).
dmatter
dmatter
Quote:
Original post by EmpireProductions
Alot of them I have made into singleton classes so I can just call their methods where I need to.
Is that your reasoning for using a singleton?

I can't help but feel you're over-complicating what should be a simple task. Why not just do things the normal way: If an instance of X needs to call a member of an instance of Y, then simply give Y to X.

Alternatively if some aspect, Z, does actually happen to crosscut every single part of the system then make it a global instance so it can be used everywhere.

There exists a theoretical scenario where something has a responsibility that crosscuts the entire system and so might be made global, but also under no circumstances must it ever be permitted to exist more than once in a given application. Only then is a singleton a suitable solution.

Would your managers and terrain engine actually blow up if I were to make two separate instances of them?
_the_phantom_
_the_phantom_
Reason to use a singleton: It is an ERROR for more than one to exist AND you REQUIRE GLOBAL access.

If you can not meet both those requirements then you usage of a singleton is a bad design choice and you should rethink things now before it becomes far far far too late.
Antheus
Antheus
The biggest con of singletons - they make
">automated testing
difficult. Anyone writing unit tests or using any kind of automated integration testing will hopefully quickly realize why this is a problem. Those that won't will have hundreds of unit tests, each of which will need to load and shutdown *entire* application to run each of the 5 line unit tests. This isn't necessarily a problem if one has a cluster for doing these tests, but quickly becomes a pain with a single computer.

Second design con of singletons - they don't scale across multiple cores. They require either explicit synchronization or transactional semantics, which will either constraint their contents to single core (via command queue or similar), or cause contention that degrades performance to single core. This issue is less common in business apps, but for computationally intensive uses, this becomes a problem.

To scale effectively across threads it is often desirable to replicate the resource on each thread. For example, logic might have one terrain engine, and renderer another, and changes are propagated from one onto another. A random discussion.

This aspect does not prevent one from using a global of some kind, but it instantly kills the "only one" aspect. Obviously, double/multi-buffering can be encapsulated inside a singleton, but it will also prove unwieldy, since by then the class will be huge, and cover hundreds of responsibilities - since it will encompass the scope of entire application, where each of the individual parts will not be "only one".

Another con of singletons in C and C++ - they can expose some obscure linking and language design issues.

Quote:
I was just wondering if it was a good idea to make these classes singletons or if theres another method I should be using
Quote:
Also is there any performance decrease with singletons


It actually doesn't matter how this is implemented. What will matter, if this is to offer any kind of performance improvement, is how you design the internal mechanics. For example, what happens if you need a resource *now* (it's on screen, needs to be displayed, player is interacting with it - but you still need to read an index, then read 17 files, then process them, then integrate them, four sequential operations).

Do you use a proxy, show blank screen, pause the simulation, start ignoring input?
davepermen
davepermen
Quote:
Original post by phantom
Reason to use a singleton: It is an ERROR for more than one to exist AND you REQUIRE GLOBAL access.

If you can not meet both those requirements then you usage of a singleton is a bad design choice and you should rethink things now before it becomes far far far too late.


and, normally, if you HAVE such requirements to any form of class, you're allready in trouble :)
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
Antheus
Antheus
Quote:
Original post by davepermen
Quote:
Original post by phantom
Reason to use a singleton: It is an ERROR for more than one to exist AND you REQUIRE GLOBAL access.

If you can not meet both those requirements then you usage of a singleton is a bad design choice and you should rethink things now before it becomes far far far too late.


and, normally, if you HAVE such requirements to any form of class, you're allready in trouble :)


Not quite - it is the fine print.

"Only one" and "global access" means "One per process" and "per-process access". The problem with this distinction is that the concept of process doesn't exist in most languages (CreateProcess is not it).

This becomes painfully obvious as soon as you need to touch multi-threading (data is local to threads and shared across process), or external resources (database, file system, sessions, authentication, running same app twice).

The distinction can be explained in this way: mail.google.com could be a singleton. After all, it is only one. But in reality, it is an email service, of which there can be arbitrarily many.

There is nothing wrong per-se with singleton assumption, as long as it is enforced and justified. Prevent same application running more than one on same OS. Write an application for GMail instead of e-mail. In those scopes, singleton concept is warranted. With experience, it can be applied more often.

But the original issues with concurrency and testing remain.
- How would you test "delete all messages" if your application is hard-coded to "user@mail.google.com"?
- How would you improve concurrency, if all your requests are sent to "mail.google.com", rather than POP3 provider which queries "pop3.a.com, pop3.b.com, pop3.c.com, ...".

The concepts implied by singleton are indeed useful, and simplify many tasks - as long as it is about one-off projects which can live with hard-coded concepts. Many projects are just that, they will not have any long term life. Other evolve, and these initial assumptions become flawed and impede the development.

And just like with other engineering problems - does the choice of using a different, long-term solution warrant the higher upfront cost? The answer to this is not simple. On one hand, there are over-engineered trivial solutions, on the other, there are projects where complexity killed any future prospects. Good, successful solutions are somewhere in between.

There just isn't a silver bullet, but the takeaway from large number of different-sized project (5-50,000 people teams and projects) is that all things being equal, globals/singleton/only-one concept should be discouraged over more open-ended solutions which defer specialization as long as reasonable.

"Singleton" decision is much broader. It is vendor lock-in, few experts vs. many specialists/technicians, niche vs. mass market, ... There is room for both, but as far as trends go, and flexibility and scale wins over long term, despite higher initial cost.
whiterook6
whiterook6
I do have one irk against singletons:
usually I go

ClassForWhichThereShallOnlyBeOne :: getInstance()->doSomething();

where getInstance() sometimes calls the constructor, which is fine and dandy. BUT what if the constructor has to take arguments? then getInstance() needs those arguments, or I need a separate function to expose them to the constructor (such as an init() function.)
Antheus
Antheus
Quote:
Original post by whiterook6

BUT what if the constructor has to take arguments?


Then it is not a singleton. A singleton always *is*. It is not constructed or destroyed - it always exists, and always has a value, like number 42 (the rest is just an implementation detail, value 42 gets loaded into register before it is manipulated, or pushed onto a stack). If singleton needs some other data, it needs to come from other singletons, just like 42 is made from a 4 and a 2, where that data also always exists.

An implementation of a singleton can be manually initialized, perhaps at application start, it can also be destroyed. But as far as application is concerned - singleton always is. This is the important part. If you need constructor, manually and explicitly create it - lazy initialization obviously doesn't work.

At lower end, anything upon which operations are idempotent can be a singleton without any ill effect. But these constructs are more commonly called value types which do not require "only one" constraint. Another version are immutable classes.

But without any of those two constraints a whole new class of problems arises, especially if immutability or idempotence constraints are flawed, such as configuration files, or logging (what if two instances are started - they will either overwrite the log file, or one will fail to open file to log), various external resources (databases, network locations), or even in-process resources (graphics resources, handles, global vs. thread local), or other issues, such as transient network timeouts, resource depletion, ....
whiterook6
whiterook6
Lazy initialization ... You're saying that
getInstance() shouldn't call the constructor when it doesn't yet exist? Intriguing.
janta
janta
Why bother thinking about choice A vs B when switching from one to the other is a matter of minutes? I'm talking about the singleton, not the global access (the later is worth thinking about!)
ApochPiQ
ApochPiQ
I'm going to say this now before things get out of hand - the instant this starts looking like it will turn hostile, I will be issuing warnings, and the thread will be closed. So play nice.


Carry on [smile]
EJH
EJH
Singleton in title = multi-page thread. I'm gonna go ahead and reference Hitler now to get it out of the way.
kRogue
kRogue
I concur with Phantom:

Quote:

Reason to use a singleton: It is an ERROR for more than one to exist AND you REQUIRE GLOBAL access.


and once you have a singleton, you need to carefully tread on it's construction, etc... generally speaking, do not do this:

static mySingletonType mysingletonobject;


but do this:

mySingletonType& getSingleton(void) {   static mySingletonType  R;   return R;}


But be aware about the fact that you have no fine control when R's deconstructor is called.

One use I have for singleton's is for a resource manager: need to get data but don't know it it has been loaded or not, then the resource manager does it for you... but even that case has evils, something has to delete the data from the manager, what happens when some of the data are objects that have, say for instance, have GL textures made o their creation, naturally then their deconstructor delete the GL textures, but then you need to make sure that the GL context still exists, worse, if we get into hairy details, you need to make sure that context is current in the thread where stuff gets deleted.... often times as not, the control freak in us does this to deal with that issue in our libraries: we create Init() and DeInit() routines...


Just tread carefully.


Close this Gamedev account, I have outgrown Gamedev.
Overburn
Overburn
i don't really see the purpose of singletons.
if a specific object is meant to exist only once, then add that in the documentation. There's no point in artificial limitations that just further complicate potentially already complicated stuff.

Maybe an experienced programmer would be able to use multiple objects of the same type in an innovative way. Which wouldn't be possible if the class was singletoned.

Say a few programmers screw up because the class isn't singletoned. Then they should read the documentation, not blame the creators of the say library because they didn't singleton the class.
Einstein once said that only the universe and human stupidity are infinite. He wasn't too sure about the universe though...

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.