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

#41 davepermen   Members   -  Reputation: 1022

Like
4Likes
Like

Posted 09 November 2012 - 06:55 AM

the streering wheel example shows that some people don't understand what a class is for, and what an object is for.

class SteeringWheel {
}

class Car {
    SteeringWheel steeringWheel;
}

classes are not what defines how many exists of something. that's the objects job.

if a singleton would fit my needs, i would use it. but it NEVER ever did. it's just a) limiting and b) making code look ugly. and, most importantly, c), it NEVER makes sense.
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


Sponsor:

#42 kunos   Crossbones+   -  Reputation: 2207

Like
0Likes
Like

Posted 09 November 2012 - 08:13 AM

the streering wheel example shows that some people don't understand what a class is for, and what an object is for.

class SteeringWheel {
}

class Car {
	SteeringWheel steeringWheel;
}

classes are not what defines how many exists of something. that's the objects job.

if a singleton would fit my needs, i would use it. but it NEVER ever did. it's just a) limiting and b) making code look ugly. and, most importantly, c), it NEVER makes sense.


this.. I am amazed what length people can go to justify their laziness. Why limit yourself to 1? I have never found a single example of something that REALLY needs to be instantiated once or kittens die... never.

That steering wheel in the car example is, seriously, pathetic.

Edited by kunos, 09 November 2012 - 08:14 AM.

Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#43 Caburé   Members   -  Reputation: 149

Like
0Likes
Like

Posted 09 November 2012 - 08:42 AM

METADATA:

There are many times when a program needs to store and reference data that is a result of processing and that can be used again. How will you store metadata? I like to use singletons for structural/constant data/flags/metadata(temporary) instances, when the complexity of creating new instances and having it loading its data from something/somewhere doesn't worth as simply acessing it straight like cache of processors is used and Windows Registry (always in memory).

#44 kunos   Crossbones+   -  Reputation: 2207

Like
0Likes
Like

Posted 09 November 2012 - 08:48 AM

METADATA:

There are many times when a program needs to store and reference data that is a result of processing and that can be used again. How will you store metadata? I like to use singletons for structural/constant data/flags/metadata(temporary) instances, when the complexity of creating new instances and having it loading its data from something/somewhere doesn't worth as simply acessing it straight like cache of processors is used and Windows Registry (always in memory).


sorry it doesn't float at all.
It doesn't have to be a ONE! You create it ONCE, and pass it around or publish it to objects interested in it.
You are talking about trading memory for computation costs.. it doesn't have anything to do with SINGLENESS and globality of scope.

Edited by kunos, 09 November 2012 - 08:51 AM.

Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#45 Caburé   Members   -  Reputation: 149

Like
-1Likes
Like

Posted 09 November 2012 - 09:02 AM


METADATA:

There are many times when a program needs to store and reference data that is a result of processing and that can be used again. How will you store metadata? I like to use singletons for structural/constant data/flags/metadata(temporary) instances, when the complexity of creating new instances and having it loading its data from something/somewhere doesn't worth as simply acessing it straight like cache of processors is used and Windows Registry (always in memory).


sorry it doesn't float at all.
It doesn't have to be a ONE! You create it ONCE, and pass it around or publish it to objects interested in it.
You are talking about trading memory for computation costs.. it doesn't have anything to do with SINGLENESS and globality of scope.



Singleton is single instance. It does not necessarily mean global scope (you can create a singleton in the scope of a package, for example).
Singleton is static - that is why there is a static word in Java (and not, again, necessarily, a global pointer/variable).
If you could reraise the data of a singleton just by making a new instance without costs, what would be the reason to use static classes (singleton) - that is what I am explaining?


How would you store, for example, trigonometry tables? If you can't see that, every time you declare a public static final int, the class where you declared it becomes a singleton, conceptually: is accessible in global scope and has only one static instance - the class itself. In Java, you can even set/change static variables at runtime (but not final variables).

I just said how I like to use SingleTons : to store metadata. I didn't argue that it is the only way to do that. But.. ppl seem to be stuck in just one technique.


After all, I am posting in the wrong place, I forgot to tell that I was meaning Java as programming language.

Edited by Caburé, 09 November 2012 - 09:14 AM.


#46 kunos   Crossbones+   -  Reputation: 2207

Like
2Likes
Like

Posted 09 November 2012 - 09:19 AM

How would you store, for example, trigonometry tables?


class TrigonometryTables
{
...
}

Posted Image
It DOES NOT HAVE to be one. If your application only needs one, you application will instance it once and make it available to its subsystems.
In other words: why making assumptions on an application behavior if you dont have to?
Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#47 Caburé   Members   -  Reputation: 149

Like
0Likes
Like

Posted 09 November 2012 - 09:33 AM


How would you store, for example, trigonometry tables?


class TrigonometryTables
{
...
}

Posted Image
It DOES NOT HAVE to be one. If your application only needs one, you application will instance it once and make it available to its subsystems.
In other words: why making assumptions on an application behavior if you dont have to?


That was a rethorical question, I already knew how to create a static class in Java. You mean why don't copy/paste Sun's/Oracle's formal definitions ? Because I thought it is a forum to interchange experience (this is rethoric again), not conceptual aspects of formal oop inspired by how philosofically the real world binds into the structure of a program and how beautiful it may become, in the aspect of fitting in the common-use of objects, the reference to singleton classes within the code.

I just said how I like to use singletons: reutilization of already created metadata valid in global level,everytime, with no specific meaning (and metadata doesn't have a context specific to an object but is the result of interactions of them, having no fit within them without extra-redesign). If the topic is how pretty it fits into the aspects of OOP... the name of the topic should be changed to "why do we still need to use singletons when it is conceptually outside the formal view of the floating round world of objects, resembling old days of procedural programming?".

I just answered the topic. If it fits or not the formal definitions of a design pattern instead of what the term means by itself.. that is another issue.

Edited by Caburé, 09 November 2012 - 09:44 AM.


#48 SiCrane   Moderators   -  Reputation: 9668

Like
3Likes
Like

Posted 09 November 2012 - 09:35 AM

Singleton is single instance. It does not necessarily mean global scope (you can create a singleton in the scope of a package, for example).

Actually global access is part of the Singleton definition. From the GoF: "Ensure a class has only one instance and provide a global point of access to it."

#49 Caburé   Members   -  Reputation: 149

Like
0Likes
Like

Posted 09 November 2012 - 09:40 AM

Global variable is already bounded in a scope.. do you mean that, when someone uses an API, all its singletons used internally must be visible for the programmer? I don't believe.

Ok. You refer a design pattern.


I was dumb and did not payed enough attention to it in the first post, in the beginning of the topic, sorry.

Edited by Caburé, 09 November 2012 - 09:42 AM.


#50 mhagain   Crossbones+   -  Reputation: 8276

Like
0Likes
Like

Posted 09 November 2012 - 09:41 AM

The fact that effort has to be made to bring up reasons (or excuses!) for using a singleton should be telling you something roundabout now.

There is no valid technical constraint enforcing "there can only be one" on anything (aside from Highlander ;) ) and plenty of valid reasons why having more than one is or can be useful. So why would you enforce such a restriction on your code? And at the expense of having to do extra work in order to do so? You're basically spending extra effort in order to impose an artificial restriction on yourself. Seems a wee bit nuts to me.

That's aside from the design considerations of using globals.

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.


#51 swiftcoder   Senior Moderators   -  Reputation: 10364

Like
1Likes
Like

Posted 09 November 2012 - 09:44 AM

That was a rethorical question, I already knew how to create a static class in Java.

He's not talking about a static class, he's talking about a normal class. Static data comes with many of its own pitfalls, even when that static data isn't truly global.

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#52 Caburé   Members   -  Reputation: 149

Like
0Likes
Like

Posted 09 November 2012 - 09:48 AM


That was a rethorical question, I already knew how to create a static class in Java.

He's not talking about a static class, he's talking about a normal class. Static data comes with many of its own pitfalls, even when that static data isn't truly global.


(I mean Java)

if I put a

public static final int x = 1;

in any normal class, that class is both global and static: is immutable because must hold the final value in at least one of its fields.. even without having static fields, in Java.

Every class in Java is static. Not every instance.

#53 kunos   Crossbones+   -  Reputation: 2207

Like
0Likes
Like

Posted 09 November 2012 - 09:48 AM

That was a rethorical question, I already knew how to create a static class in Java.


Posted Image who said anything about static classes?
My entire point is to avoid static stuff actually :P . What I meant was, what do you use to store your precomputed values? A standard class, plain and simple.
Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#54 Caburé   Members   -  Reputation: 149

Like
0Likes
Like

Posted 09 November 2012 - 09:52 AM


That was a rethorical question, I already knew how to create a static class in Java.


Posted Image who said anything about static classes?
My entire point is to avoid static stuff actually Posted Image . What I meant was, what do you use to store your precomputed values? A standard class, plain and simple.


Yes, you are right.

#55 mhagain   Crossbones+   -  Reputation: 8276

Like
4Likes
Like

Posted 09 November 2012 - 10:23 AM

If you want to store precomputed values, and if you need to store them globally, then how about just not using a class at all? You don't need a class to store precomputed lookup tables; you can store them in regular standalone variables.

http://en.wikipedia....er_programming)

A class defines constituent members which enable its instances to have state and behavior.


A precomputed lookup table has neither state nor behaviour; it's just a list of static values. They never change, you don't operate on them, and nothing in the table defines an operation on anything else. So make them variables, make them standalone, make them const; a class is not needed.

As I indicated earlier, using a class for this is a clear case of shoe-horning OO design onto something that is not OO in nature, and to my mind that's a far bigger design smell than using globals (which actually could be seen as appropriate given the two assumptions I started out with here).

Of course, that breaks down in the face of languages that enforce use of classes for everything, at which point you have to do what is mandated by your chosen language. But if you don't have to do it, if you don't need to do it, then why do it?

Edited by mhagain, 09 November 2012 - 10:25 AM.

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.


#56 Josh Petrie   Moderators   -  Reputation: 3840

Like
6Likes
Like

Posted 09 November 2012 - 11:14 AM

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?


Ah, but like I noted before, I would argue here that the problem is with the design whereby "the Player class should be able to call a function in Graphics when a key is pressed." That's giving the player object responsibility over two very distinct systems (graphics and input), especially when the player appearance to be nothing more in this design than an entity within the logical world. You are now conflating, and thus coupling, your logical objects with your input and your rendering. With a bunch of singletons/globals this is less obvious, but when you remove them as you have suggested here, you discover those dependencies.

Ideally all three of those (logic, rendering, input processing) should be independent of each other. Communication between them should occur via the higher level container for each subsystem (in this case, the game itself, so perhaps in your "game" class).

The input system collects a bunch of keyboard events, or whatnot. Your game translates those (maybe via some kind of action mapper). Then it asks the world for the currently-controlled entity and uses the public interface of that entity to apply the actions. Once all that's done and the rest of the world goes through it's simulation step, the game collects all the visible/renderable entities and constructs (or updates) rendering descriptions for them, and feeds those rendering descriptions to the renderer object itself.

In this system:
  • The "Player" class makes the most sense as the action mapper; all it has are a pointer to the currently-controlled world entity, and a bunch of methods to translate unmapped key codes into actions based on the entity state (for example, making the "V" key either draw or stow the entity's weapons, depending on the entity's current "are weapons drawn" state). The responsibility of the player is to change entity state.
  • World logic, input logic and rendering are all independant, tied together at the topmost level by the game itself, which uses a Player object to translate between input and world systems.
  • Similarly, the topmost game object also feeds input to the graphics by iterating all the active and renderable game entities and creating render descriptions (sprites, for example) using that data. The game determines whether to use animation sequence 0 (weapons stowed) or 1 (weapons drawn) for an entity based on that entity's state, but the graphics system only knows about and only has to see a bunch of sprites.
You arrive at the quandary you posed about the player object by incorrectly taking the OO mentality too far -- OO design is about modelling objects in real-world terms, but only to extent. Your initial description of a Player does model a human player very literally -- it is the human player who, however indirectly, causes graphics to change when they press keys. But the in-code version of that object shouldn't be developed that literally, because it leads to exactly the kind of spaghetti of dependencies you encountered.

Josh Petrie | Game Developer, Undead Labs


#57 Dave   Members   -  Reputation: 1527

Like
4Likes
Like

Posted 09 November 2012 - 05:05 PM

I don't see that singletons or globals are particularly bad. I've worked on commercial games where singletons and globals have been used. One used them wisely and was the easiest codebase that i've ever worked on and one used it badly and it was extremely complicated and hard to ramp up on. I have also worked on commerical projects where "correct" OOP has been used and overused and this was also one of the hardest codebases i've worked on.

You can write bad code with "correct" practices. You can write great code with "bad" practices.

Writing tight OOP code in studios where iteration time is important and year on year you're rewriting large portions of code is common because of new platforms and new titles is a complete waste of time and it is important that you make simple architectural decisions that:

a) Allow new programmers to get on with things easily.
b) Make it easy to add features to.

It is argued that you can screw things up if you start touching global variables that you shouldn't. If you hire programmers with an ounce of common sense they generally won't do that and no matter what "barriers" you put in place to protect programmers from themselves, if a shortcut is really desired then it will be found. The friend keyword comes out in no time.

Stop worrying about writing perfect OOP and just get on with writing cool stuff for your game/engine.

Edited by Dave, 09 November 2012 - 05:07 PM.


#58 phantom   Moderators   -  Reputation: 7563

Like
2Likes
Like

Posted 09 November 2012 - 07:02 PM

Writing tight OOP code in studios where iteration time is important and year on year you're rewriting large portions of code is common because of new platforms and new titles is a complete waste of time and it is important that you make simple architectural decisions that:


Except every time I hear the 'new code because of new platforms' arguement is I can't help but think it is garbage.

Consoles have been stable for many years now and it's only recently that a nod towards the new ones has happened. We've had ~7 years of stability in that domain.
Windows is an even bigger pile of fun as DX9 was stable for the better part of a decade and again it is only in the last few years that DX11 has become 'a thing'.
Other platforms come along at a much lower frequency and, with correct abstraction, are not a case of 'rewrite large portions of code' but rework layers which are needed to get them up and running.

I would argue that by NOT taking time to design things properly up front people are wasting more time with rewrites and rebuilds of systems which don't need it. The 'get it done' mindset has its uses but hacking on things is not, in itself, a good way to go unless you want to waste time over the course of years rebuilding the problem.

a) Allow new programmers to get on with things easily.
b) Make it easy to add features to.


Except none of this is an arguement against designing things properly nor, more importantly when it comes to this thread, is it an arguement in favour of singletons which, in my experiance, have only served to cause more harm than good.

A correctly designed system can allow for BOTH of those things IFF it is correctly designed.
And by that I don't mean the classic 'just out of university' OOP method of 'zomg! classes for everyone!' but a sensible division of code and responcibility. People get too hung up on 'OOP = class' and lose track of the real meaning of the design methodology. More to the point they focus purely on 'put everything in a class and give it methods' than correctly splitting things up into classes, structures and free functions as required and, more importantly, ignore data flows and how information needs to move around the system.

In short; most people in the world write utterly shit OOP but that doesn't mean it isn't useful to design larger subsystems for it.

It is argued that you can screw things up if you start touching global variables that you shouldn't. If you hire programmers with an ounce of common sense they generally won't do that and no matter what "barriers" you put in place to protect programmers from themselves, if a shortcut is really desired then it will be found. The friend keyword comes out in no time.


The problem is that most people AREN'T good enough to not make mistakes and do it wrong.
I have seen some wonderful things where people fresh out of university have commited crimes against code so bad it makes me wonder why they are allowed to even write code in the first place. (My favorite being the guy who decided to, in his branch without telling anyone, change the interface to our lua subsystem from function(luaObject, ....) syntax to luaObject->function(....) syntax while at the same time weaving his work around the changes. It took me 4 HOURS to get his code back into mainline for a merge and I no longer trust fresh faced programmers not to fuck up in some massive manner)

So, yes, people make mistakes and yes there are ways to hack around the 'protection' BUT with a properly designed system the immediate solution is often cleaner, easier to maintain and, more importantly, does NOT lead them down the path of thinking 'hey, others have done it so it's probably ok for me to do so...' because they learn by example.

Truth of the matter is that most people in the industry can't design for crap - unfortunately if we tried to staff an industry with people who were good at it we'd soon run out of people so in order to save time later its best to discourage bad code to start with.

And to that end... a story!

In our engine we were lacking a sane method of the game team to transfer values from the logic thread to the renderer thread (and thus the GPU).
We took a day to hash out WHAT we wanted the system to do (I'll spare you the details, because frankly they are a tad confusing at times), roughly how we wanted it to interact and then I got cracking on the design and implementation details.
The two senior members of the team feedback into the process every few days as we discussed things and by the end of a two month period we had a working solution (with some of the most complicated multi-pass binding/patch table setup code I've written in my life BUT...) which was created when the renderer started up and cleanly passed, by reference, to the subsystems which required it.

However we weren't finished there because the aim for this system was to replace ALL the variables which could be passed to shaders; this included built in tokens for things like shadow map textures as well as the post-processing system's frame buffer textures and variables.

The 'global texture cache' (as I believe it was called) had been written partly by a senior and finished off by a junior. Frankly the code was overly complex for what it did BUT more importantly it had been implemented as a global system and because of this it had inflitrated multiple levels of code with no clarity as to the flow. It took LONGER for me to remove the old code and clean up the mess it had made than it took me to write the addition to the Parameter System which had been written; Clean OO intergration 1 v Global Code 0.
(The new system also expanded the abilities of the GTC functionality bring it in line with the general parameters affording the game team more control).

Post Processing was the same story; the functionality added to the Parameter System took maybe 2 days to add with no major interface changes aside from some special casing due to the stupid way the Post Processing was implemented.

Finally a few months back we needed a feature adding to the Parameter System at the request of the game team. The conversation as to if I should even add the feature took LONGER than the time it took me to write the feature, integrate it, test it and push it into the publish queue, the system was that extendable.

The system itself was built on both OOD and DOD principles to use classes when it made sense and pay attention to the data when it didn't.
The system was written in Jan/Feb of this year with a couple of weeks in March to integrate the extra functionality and remove the old.

This system is a lynch pin in the game F1 All Stars which is coming out soon; if it breaks then we lose logic->renderer data flow so it's one of the most critical systems in the whole engine right now.

The system has had ZERO time spent on bug fixing; not a single bug has been filed against it and when suspicion has been cast it has been easy to prove (despite the mad binding code which while it looks complex does work flawlessly) it wasn't a problem.

Now, we could have hacked together a system in a month, thrown it in as a global and watched as everyone abused it and dealt with bugs left, right and centre BUT instead we decided to do it right and because of that the integration is clean, the code is robust and its freed me up to work on other bits of genius instead.

#59 Khatharr   Crossbones+   -  Reputation: 3038

Like
0Likes
Like

Posted 09 November 2012 - 09:46 PM


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.


Because you can only have one namespace with the same name per program.
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.

#60 Josh Petrie   Moderators   -  Reputation: 3840

Like
0Likes
Like

Posted 09 November 2012 - 10:59 PM



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.


Because you can only have one namespace with the same name per program.


Which does nothing to enforce that a type defined within that namespace cannot be instantiated multiple times:

namespace N {
  struct C {
    int someMemberVariable;

    void someMemberFunction ();
  };
}

int main () {
  C c1;
  C c2;
}

// ...

The only way you could really guarantee uniqueness out of this is to do something like:

namespace N {
namespace C {
  extern int someQuoteMemberQuoteVariable;

  void someQuoteMemberUnquoteFunction ();
}
}

Is that what you mean?

That transform C from an aggregate type into a namespace containing free functions (and freestanding global "members" which presumably in a real implementation you'd not actually expose in such a fashion).

That means you have only a single thing that "C" represents, but it's no different from classical procedural programming there, and subject to the same pros and cons. Much of the C runtime library functions that maintain state -- like rand() -- are essentially an implementation of this technique sans the namespace (which just provides a name scope and some organizational stability), and have many of the same problems a singleton can.

Edited by Josh Petrie, 09 November 2012 - 10:59 PM.

Josh Petrie | Game Developer, Undead Labs





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