questions about singletons

Started by
39 comments, last by Khatharr 10 years, 7 months ago

questions about singletons:

1. the code that makes it so you can only create one singleton is just to prevent you from doing something stupid like creating two entity lists by accident, correct?

2. are singletons considered good, bad, indifferent?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement

There are two parts to being a singleton: one is that you can only create one of that class, the other is that the class has a single point of global access. If if you can create only one of a class but it doesn't have global access it's not a singleton.

As a whole singletons seem to be regarded as a bad pattern due to the global access part. Global state is bad because it complicates multi-threaded code, makes testing and debugging more difficult and frequently makes code maintenance and refactoring a pain. The single instance of a class part is largely regarded as usually pointless but not harmful in and of itself.

1. the code that makes it so you can only create one singleton is just to prevent you from doing something stupid like creating two entity lists by accident, correct?

Yep along with the Global Access property SiCrane mentioned. The third property of the Singleton paradigm is control over the relative order of construction of singleton objects, something that's not possible with object instances that are declared as globals. As with most design paradigms Singletons are mostly used to show intent to would be modifiers of the system rather than to prevent bonehead mistakes by making them syntactically illegal.

As a whole singletons seem to be regarded as a bad pattern due to the global access part. Global state is bad because it complicates multi-threaded code, makes testing and debugging more difficult and frequently makes code maintenance and refactoring a pain. The single instance of a class part is largely regarded as usually pointless but not harmful in and of itself.

Mostly agree in the academic sense but in practice I would say they have their uses stemming primarily from convenience. Sometimes you truly only want one instancing of something running around (assert tracker, GPU wrapper etc) in which case having a single point of access keeps things simple. Avoiding the threading pit-falls can be done by proper constructing the singleton to deal with multi-threaded access and being vigilant about keeping singleton access out of code that doesn't absolutely need it.

I'll add, that one of the great dangers of global state is not just that it makes reasoning about your program hard, but that it allows you to code merrily along without having to stop and reason about how your program should be before you write it. That is, if you can just access the data willy-nilly from anywhere, you never have to stop and ask yourself if that's a good idea, and as a result, data that maybe shouldn't be accessed directly from deep inside your program takes root there. Like any common weed, the deeper and more numerous the roots, the harder it is to remove. This is what makes re-factoring global state like singletons difficult.

Global state as a singleton adds another dangerous aspect to the problem, which is that many less-experienced programmers allow themselves the delusion that, "since Singleton is an 'OOP Pattern' that was even written about in a book once", that it is somehow less dangerous that global state in the form of simple globals. They think it must be OK if its a Pattern. In fact it is almost always an anti-pattern -- something that even the authors that popularized it now concede. Taken with the additional (if sometimes harmless) restriction on it being a unique representation of that information or resource, global state in the form of a singleton is nearly always worse than global state in the form of a common global (though both are very bad to begin with; its a bit like asking whether The Penguin or The Joker was more evil.)

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


Sometimes you truly only want one instancing of something running around (assert tracker, GPU wrapper etc)

This is misleading -- You may "want" only one, but it's very, very seldom that one cannot imagine a scenario where, in fact, you actually need more than one, and even more seldom when having more than one would actually be incorrect. Usually when a programmer says "I'll use a singleton because I only want one of these." what he's really saying is "I'll use a singleton because I don't want to bother making my code robust enough to handle more than one of these."

If you can justify choosing convenience over correctness you're free to do so, but you've made your bed once you've chosen a Singleton and you alone will have to lay in it.

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


Sometimes you truly only want one instancing of something running around (assert tracker, GPU wrapper etc)

This is misleading -- You may "want" only one, but it's very, very seldom that one cannot imagine a scenario where, in fact, you actually need more than one, and even more seldom when having more than one would actually be incorrect. Usually when a programmer says "I'll use a singleton because I only want one of these." what he's really saying is "I'll use a singleton because I don't want to bother making my code robust enough to handle more than one of these."

If you can justify choosing convenience over correctness you're free to do so, but you've made your bed once you've chosen a Singleton and you alone will have to lay in it.

...yeah, 'correctness' in software design; something that's largely considered undefinable. We are not scientist doing good science or bad science; we are craftsman and what one paradigm holds over the other is simply the kinds of coding decisions that are discouraged vs encouraged. The idea is to use designs that encourage use cases that pay productivity dividends down the road and discourage use cases that become time sinks. In this sense Singletons are ALL BAD but making your life easier down the road shouldn't be a directive that is pathologically perused such that the productivity of the moment is drawn to a crawl. I guess the point I'm trying to make is that a tipping point exists between writing infinitely sustainable code and getting the job done. Here are some examples:

Singletons make sense for something like a GPU; it is a piece of hardware, there is only one of them and it has an internal state. Of course you can write code such that the GPU appears virtualized and your application can instantiate any number of GPU objects and use them at will. Indeed I would consider this good practice as it will force the code that relies on the GPU to be written better and should extend nicely to multi GPU platforms. The flip side is that implementing all this properly comes at a cost in productivity at the moment and that cost needs to be considered over the probability of seeing the benefit.

Another example is collecting telemetry or profiling data. It's nice to have something in place that tells everyone on the team: "Hey, telemetry data and performance measurements should be coalesced through these systems for the purposes of generating comprehensive reports." A Singleton does this while a class declaration called Profiler and Telemetry does not. Again, you can put the burden of managing and using instances of Profiler and Telemetry onto the various subsystem of your application and once again this may lead to better code but if the project never lives long enough to see that 'better code' pay productivity gains then what was the point?

I don't implement Singletons either personally or professionally (for the reasons outlined by Ravyne and SiCrane unless explicitly directed to do so) but I have worked on projects that did use them and overall I was glad they existed as they made me more productive on the whole. In these instances the dangers of using other people's singletons in already singleton dependent systems never came to fruition and the time sink I made writing beautiful, self contained, stateless and singleton free code never paid off. Academic excellence vs. pragmatism: it's a tradeoff worth considering. Mostly I'm playing devils advocate here as I find blanket statements about a design paradigm being all good or all bad misleading. Anyway, this is likely to get flamey...it already is and people aren't even disagreeing yet. :) I'm out.

To be honest I just have very rarely(and by rarely I mean once, in C#) found a real use for a singleton and in that case it was more to cover a problem I couldn't get around, I think it related to a certain global having state or something. It had good reasoning at the time whatever it was.

Other than that a singleton is really just a global under a really thin blanket. There's no real reason architecture wise that you should need something to only function if it is the only copy of itself. Globals can be useful, sure, but the golden rule with globals usually is that they are objects that won't change, such as constants.

Of course even in that case they shouldn't really be bare globals, placing things in namespaces is always a helpful idea, and a global only really becomes a global if you include it anyway, otherwise your unit of code might as well not see that it exists.

In general using either a singleton or a global as a point of access for something just doesn't have much of a point, you can accomplish the same thing with dependency passing. I used to feel that it was messy to pass a top interface class like an "engine" or something abstract like that around to allow access to other objects but the fact is that such an object keeps the dependencies bundled together. When you use a full on global object or a singleton you're basically shooting it up into space and letting everyone touch it without you having a clue where they're touching it from or how much code you will break if you modify or remove it.

We may not be able to imperially measure the goodness of the code we write and systems we design, but we do have a reasonably strong understanding of what's good none-the-less. If we could make perfect statements about what is--now--and will remain--forever--single, then Singleton at least wouldn't introduce that very dangerous assumption. The trouble is: we can't. Not many years ago, the assumption that the GPU was singular in any system might have seemed reasonable. Now, multi-GPU setups are common, things like nVidia Optimus are common (switchable integrated+discrete GPUs), and Hydra (allocating graphics calls among multiple GPUs, even from different vendors) is a thing. Assumptions very often turn out wrong.

So now the point may be argued, correctly, that 'forever' is an awful long time to be concerned about. And it is. Probably most software (gaming, anyways) will be developed over a period of 12-36 months, and have an active support cycle of about twice that if the game continues to generate revenue. So, already we are predicting 3-6 years into the future. Now, if we base our next game on the same codebase, we can extend that number out another 12-36 months per iteration. We're now predicting things 4-9 years into the future. Not forever, but in a realm very few people are consistently right at guessing about.

So, if we are considering the potential productivity costs of choosing Singleton or not, we have to also consider the unknown cost of guessing wrongly about the uniqueness of the thing, multiplied by the likelihood of our being wrong. On the other hand, if we do not make the simplifying assumption of its singleness, then we do pay a cost now, but we probably have a decent idea of what it is and being more general, rather than less, is not likely to cause us any unexpected grief later on.

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

The third property of the Singleton paradigm is control over the relative order of construction of singleton objects, something that's not possible with object instances that are declared as globals.

That's not a property of a singleton. That's an implementation detail that some (not all) singletons have that can be extended to non-singleton objects.


The third property of the Singleton paradigm is control over the relative order of construction of singleton objects, something that's not possible with object instances that are declared as globals.

so its supposed to be manually new'ed on the heap by the coder so they control when the constructor runs?

as opposed to some arbitrary order decided upon by the compiler for object instances declared global static?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement