views:

302

answers:

6

All of a sudden I'm having a bit of an OO crisis. Over the last couple of years I've made quite good use of Singleton objects. I used them in many places.

For example, in designing an MVC Java application, I'd create a Singleton 'SystemRegistry' class to store the model and view classes (I've only worked on simple apps and the need for multiple views never came up).

When I create my model and view objects (which weren't singletons, just normal objects), I'd do something like:

SystemRegistry.getInstance().setModel(model);

In my controller classes (which were pretty much event handlers for different GUI items), I'd get access to the view or model as follows:

SystemRegistry.getInstance().getView();

I would never use the SystemRegistry class in the model portion of my app but would, at times, use it in my view to access (but rarely, if ever, to modify) information from the model.

From what I've read (notably Steve Yegge's article), this seems like a poor way to design my application. Any ideas as to better ways of structuring my code.

Also, another aspect of how I design classes, which may, or may not be related to Singletons, is the use of 'Manager-type' classes. An example is a (very simple) OpenGL-based game engine I created in C++.

The main class was GameEngine. It was the over-arcing class that stored a bunch of Managers and handled the main loop and what not. Some of the Managers stored in this class were things like: ObjectManager, RenderingManager, LightingManager, EventManager (includes input), HUDManager, FrameRateManager, WindowManager, etc. There were probably a few more as well.

Basically these classes handled the different aspects of the game engine. The names are pretty straightforward so you should be able to get a good idea of how they're used.

Now, this was meant to be a reusable base that I could use in different projects with the need to change it ideally.

In each new game, I would create an instance of the GameEngine as a class-wide variable (most of the game logic was stored in a single class) and set up the different managers (for example, loading the window co-ordinates or lighting details from a file, setting the FPS, etc). To register an object in the ObjectManager I would do something like:

Player player = new Player();
gameEngine.getObjectManager().addObject(player);

This object will now be stored in a vector in the ObjectManager class and will be drawn when the GameEngine calls the ObjectManager drawObjects() method in each frame.

I might've gotten a bit paranoid now after that article on Singletons (and might not have had enough time to wrap my head around it), but I'm starting to second guess and wonder if the way I designed my GameEngine was proper (for lack of a better word) and didn't just fall into the same pitfalls shared by the Singleton pattern.

Any comments on my post would be much appreciated.

Edit: Thanks for the answers. I appreciate them greatly. If possible, I'd love if someone could give me some hints regarding the two project scenarios posted above. How could I have avoided the use of Singletons / Managers?

With the first one, would DI have been the correct response? Should I have even given the view access to the model (this is probably more of an MVC response)? Would the view benefit from implementing an interface (so that multiple different views can be plugged in)?

In the second case, how else could one have structured the application? Is the gripe simply the use of Manager classes as opposed to more specific names? Or is it that, in some cases, the classes can be further broken-down (e.g. ObjectHolder, ObjectDrawer, ObjectUpdater)?

+3  A: 

Steve Yegge is right. Google goes even further: they've got a Singleton detector to help root them out.

I think there are a few problems with Singletons:

  1. Harder to test.
  2. Stateful Singletons have to be careful with thread safety; easy to become a bottleneck.

But sometimes you really do have to have just one. I'd just not get carried away with Small Boy With A Pattern Syndrome.

duffymo
+1 But then if you have the need for just one, dependency injection is the way to go, and with modern DI frameworks adds only so little in terms of complexity.
Helper Method
Exactly. Spring has Singletons, but they aren't the same as writing your own.
duffymo
Even when you "have to have just one", that still doesn't mean you need a singleton. Even without DI, passing one more argument to the constructor usually isn't as big a deal as it might seem up front.
jalf
+1  A: 

I had a similar sort of realisation somewhat recently when looking over one of my projects and seeing the proliferation of 'manager' classes (mostly singletons). It seems that your concerns are twofold - firstly, whether to use global state at all (leading to manager type classes), and secondly, whether singletons are the best choice for global state.

I don't think it's really black and white. Sometimes you have entities which really should have exactly one instance and should be accessible from everywhere in your program, in which case, a global state solution makes sense. And it can be convenient if the non-global alternative involves creating an instance of your entity and passing around references to it through layers upon layers of methods and constructors (and hierarchies of class constructors ugh).

On the other hand, one needs to consider the evils of global state. It hides dependencies. As soon as you introduce globals, functions are no longer black boxes whose input is restricted to their parameters. And if you have mutable global state which can be accessed by multiple classes in multiple threads in any order, you can have a debugging nightmare on your hands. And this all makes comprehension/testing more difficult.

When opting for global state, sometimes a static class is a better choice than a singleton (there are other options too). For one, it saves extra GetInstance() invocations... and to me, it just feels more natural. But it's worth noting that singletons automatically allow for lazy loading, which can be important if initialisation of your global entity is rather heavyweight. Furthermore (and somewhat contrived here), should you need to provide variants of your global behaviour, you can simply subclass your singleton.

Nathan
+7  A: 

Well, why did you write singletons? If you understand what went wrong in your singletonitis, you know what to watch out for in the future.

Why did you believe for a second that singletons were a good solution to any kind of problem? Because a book said so? Don't blindly trust books. Books need to justify their claims just like anyone else. If I tell you your code looks much better if you turn your monitor upside down, the burden of proof is on me. Even if I'm some kind of code god. Even if millions of developers across the world worship me daily, my advice is still worth nothing if I can't justify it, and if I can't make you go "ok, that makes sense. I understand why you recommend this, and I can't think of a better way to do it".

And the same is true for a certain design patterns book. So they wrote that "design patterns are awesome", and that "the singleton is a design pattern, and therefore it, too, is awesome". So what? Can they justify this claim (no they can't, at least not in the singleton case), so ignore it.

If someone else suggested that you use a singleton, the logic is the same: did these people actually present a good argument for why it was a good idea?

And if you came up with a justification yourself, can you, today, see what was wrong with it? What did you forget to take into account?

The only way to avoid making too many mistakes in the future is to learn from the mistakes you've already made. How did singletons or manager classes slip by your defenses? What should you have been watching out for when you first learned about them? Or for that matter, later on, when you were using them liberally in your code. What warning signs were there that should have made you wonder "are these singletons really the right way to go?" As a programmer, you have to trust your own mind. You have to trust that you'll make sensible decisions. And the only way to do that is to learn from the bad decisions when you make them. Because we all do, all too often. The best we can do is to make sure we won't make the same bad decisions repeatedly.

Regarding manager classes, their problem is that every class should have exactly one responsibility. What is the responsibility of a "manager class"? It.... uh.... manages stuff. If you can't define a concise area of responsibility, then the class is wrong. What exactly needs managing about these objects? What does it mean to manage them? The standard library already provides container classes for storing a group of objects.

Then you need a bit of code with the responsibility for drawing all the objects stored there. But that doesn't have to be the same object as stores the objects.

What else needs managing? Find out what "managing" these objects means, and then you know what classes you need to do the managing. It's most likely not a single monolithic task, but several different kinds of responsibilities, which should be split out into different classes.

Regarding singletons, I won't repeat what I've said so many times before, so here's a link.

One final piece of advice:

Screw OOP. Really. Good code is not synonymous with OOP. Sometimes classes are a nice tool for the job. Sometimes, they just clutter everything up, and bury the simplest pieces of code behind endless layers of abstraction.

Look into other paradigms. If you're working in C++, you need to know about generic programming. The STL and Boost are nice examples of how to exploit generic programming to write code to solve many problems that is both cleaner, better and more efficient than the equivalent OOP code.

And regardless of language, there are many valuable lessons to be learned from functional programming.

And sometimes, plain old procedural programming is just the nice and simple tool you need.

The key to "good design" is not to attempt "good OO design". If you do that, you lock yourself into that one paradigm. You might as well try to find a good way to build a house using a hammer. I'm not saying it's not possible, but there are much better ways to build much better houses if you also allow yourself to use other tools.

As for your Edit:

With the first one, would DI have been the correct response? Should I have even given the view access to the model (this is probably more of an MVC response)? Would the view benefit from implementing an interface (so that multiple different views can be plugged in)?

DI would have been one option for avoiding singletons. But don't forget the old-fashioned low-tech option: simply manually pass a reference to the objects that need to know about your "former" singleton.

Your renderer needs to know about the list of game objects. (Or does it really? Maybe it just has one single method that needs to be given the list of objects. Maybe the renderer class in itself doesn't need it) So the renderer's constructor should be given a reference to that list. That's really all there is to it. When X needs access to Y, you pass it a reference to Y in a constructor of function parameter. The nice thing about this approach, compared to DI is that you make your dependencies painfully explicit. You know that the renderer knows about the list of renderable objects, because you can see the reference being passed to the constructor. And you had to write this dependency out, giving you a great opportunity to stop and ask "is this necessary"? And you have a motivation for eliminating dependencies: it means less typing!

Many of the dependencies you end up with when using singletons or DI are unnecessary. Both of these tools make it nice and easy to propagate dependencies between different modules, and so that's what you do. And then you end up with a design where your renderer knows about keyboard input, and the input handler has to know how to save the game.

Another possible shortcoming of DI is a simple question of technology. Not all languages have good DI libraries available. Some language make it near impossible to write a robust and generic DI library.

In the second case, how else could one have structured the application? Is the gripe simply the use of Manager classes as opposed to more specific names? Or is it that, in some cases, the classes can be further broken-down (e.g. ObjectHolder, ObjectDrawer, ObjectUpdater)?

I think simply renaming them is a good start, yes. Like I said above, what does it mean to "manage" your objects? If I manage those objects, what am I expected to do? The three classes you mention sound like a good division. Of course, when you dig into designing those classes, you may wonder why you even need an ObjectHolder. Isn't that exactly what the standard library container/collection classes do? Do you need a dedicated class for "holding objects", or can you get away with simply using a List<GameObject>?

So I think both your options really come down to the same thing. If you can give the class a more specific name, then you probably don't need to split it out into multiple smaller classes. But if the you can't think of a single name that makes it clear what the class is supposed to do, then it probably needs to be broken down into multiple classes.

Imagine that you put your code away for half a year. When you come back to it, will you have any idea what the purpose of the "ObjectManager" class was? Probably not, but you'll have a pretty good idea what the "ObjectRenderer" is for. It renders objects.

jalf
+1 "Screw OOP" is a breath of fresh air. Too many people automatically and unconditionally equate "OOP" with "good". Bjarne Stroustrup wrote about this more than once.
FredOverflow
A: 

The way I decide if I want a singleton or not is when I answer the question: "If I'd instance my application twice in the same runtime, would it make sense?", if the answer is yes then I use it.

dukeofgaming
A: 

A two-step-answer:

1) http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/

Singletons are good design if they are created to ensure only one instance exists. They are bad design if they are created to ensure global access.

2) http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/

Global access is countered by using dependency injection.

koen
can you give me an example of when you need to "ensure only one instance exists"? It is only good design to ensure that when you actually *need* that guarantee. And I can't think of a single case where you need that.
jalf
The usual examples are "application settings" - you don't want one part of the app changing the background colour in one instance, but another part of the app looks up the background colour in a different instance - "inventory", "system log" and so on.
Kate Gregory
@jalf another typical example is a database connection
koen
In both cases, I see no reason why you **cannot* be allowd to have two. App settings seems like an *obvious* counterexample, even. I have the set of settings that are currently enabled, and then there are the ones I'm currently setting in the application's Option screen. They shouldn't be applied until I hit 'ok', but I don't see why they shouldn't be kept in the same "application settings" class as the "active" settings
jalf
Likewise for database connections. I *often* want just one global connection pool, sure, but what's to say that this will *always* suffice? What if I sometimes want to have two smaller pools, so that I'm guaranteed that queries made on one pool can never starve out queries waiting for a free connection on the other pool? It makes perfect sense in both examples to *create one* of these objects. But I don't agree that it makes sense to prevent the programmer *at compile-time* from creating a second instance.
jalf
@jalf If it makes sense to have a second instance you don't make it a singleton. If it makes sense to have only one you can make it a singleton.
koen
@koen: do you often try to sabotage future extensions to your own code? You don't need to have coded for long to know that what "makes sense" today might not make sense tomorrow. You might have overlooked a vital part of the requirements. The requirements might change. If I have the choice between a piece of code that works with one *or* two instances, versus one that is *unable* to work with two instances, I choose the former *every time*. There is no downside to it. It can do everything the singleton can, *in addition* to removing a potentially crippling limitation.
jalf
If it makes sense to have only one instance, then you *create* only one instance. Problem solved. That way, as long as it makes sense to have only one instance, that is exactly what you have. And when you realize that oops, it would be convenient to have two instances here, you don't need to rewrite the code. You can just create a second instance. There is just nothing gained from artificially *removing* flexibility and genericity from your code.
jalf
+3  A: 

Regarding your original question all has been said, so this about the followup question in your edit.

Do not confuse having only one instance of a class with the singleton pattern. In your case, it is fine to have a manager class that manages all kinds of things. It is also totally cool to only have one instance it for your whole application. But classes that use the manager should not rely on the existence of exactly one global object (be it via a static variable for the class or a global variable that is accessed by all). To mitigate this, make them require a reference to a manager-object when they are constructed. They can all receive a reference to the same object, they won't care. This has two great side-effects. First, you can test your classes better, because you can easily inject a mock-manager object. The second benefit is that, if you decide so later, you can still use more than one manager object (e.g. in multi-threading scenarios) without having to change the clients.

Space_C0wb0y