Managers, which pattern should I use

Started by
28 comments, last by Khatharr 10 years, 8 months ago

If you need simplified access to a lot of subsystems and yet you do not want to place restrictions on directly accessing the subsystems, a facade pattern might work. Create a class that allows you to assign subsystem objects, preferably as interfaces. The facade merely holds a reference to the subsystem object but doesn't prevent you from accessing the object directly if it becomes necessary. You can then add methods to the facade that allow you to control the complex behavior.

Advertisement

Ok, if I understand it, I have to create one class which handle Input from keyboard, one from mouse, gamepad etc. and put this classes inside a facade and this facade class put inside a root class? And if some class want keyboard input I should pass it by parameter? Something like this:


class Keyboard
{
//some members variables, functions etc.
};
class Mouse
{
//some members variables, functions etc.
};
class Gamepad
{
//some members variables, functions etc.
};

//Facade class
class InputHandler
{
Gamepad m_Gamepad;
Mouse m_Mouse;
Keyboard m_Keyboard;
//other functions, variables...
};

//Root Class
class Game
{
InputHandler m_InputHandler;
};


Ok, if I understand it, I have to create one class which handle Input from keyboard, one from mouse, gamepad etc. and put this classes inside a facade and this facade class put inside a root class? And if some class want keyboard input I should pass it by parameter? Something like this:
Why?

That is certainly one way to do it, but far from the only way.

Games generally like to handle events and flags.

* You care that you got a "move left" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You care that you got a "fire" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You care that you got a "zoom in" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You care that you got an "object placed" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You generally don't care if the player prefers WASD or IJKL or arrow keys for a keyboard mapping.

* You generally don't care if the player prefers to reverse the up/down axis for a controller

Large games tend to have multiple input mappings that generate a common set of events or states that are consumed or tested.

The code tests against that common interface and not against a specific keyboard or controller.

I had input manager and texture manager in one big root class, and pass them to function which want to use them, but this is a bad or not? Is some pattern which should I use?

The problem with this is the big root class. It would be better to pass input manager and texture manager as separate objects to functions and classes that need them. Don't pass both together, unless both are needed.

Others have made good points too, particularly about how "manager" is not a good name and what frob said about how input should work.

There was a time (not too long ago) when I couldn't figure out how to design a game without every manager being a singleton. It severely hampered progress, because global access led to global use, and bugs became ridiculously difficult to track down.

Then I learned about the Single Responsibility Principle, and saw how managers violate it. What does it mean to "manage" resources or input? Take a notebook and a writing device and write down everything you expect your texture manager will need to do. Repeat for your other managers. Each action listed for a given manager is likely to be an atomic responsibility, and should strongly be considered a candidate for its own class.

So maybe you have a TextureFetcher class that you can instantiate to request a given texture. And TextureFetchers have a static TextureCache member so that you can instantiate a TextureFetcher wherever you need to (some rendering class or preload routine) and they will always have a handle to the same TextureCache where all the textures are stored. And if the TextureCache doesn't currently have the requested texture loaded, it can instantiate a TextureLoader, or use one instantiated in its constructor, to load the texture. No globals; the closest thing is a static member, which can be private and really only be called from a couple methods in the Fetcher. Nice and easily trackable code.

That's kinda off the top of my head and should be considered more an indication of how globals aren't necessary rather than an example of How To Structure Your Code.

Another couple of concepts to get familiar with are Inversion of Control and Dependency Injection. I used to think passing pointers and references all over the place made messy, ugly code. But it makes dependencies explicit rather than trying to keep track of everywhere a global singleton has been called. And in practice, with a bit of planning, they don't need to be passed around as much as you may fear.

There's probably a use for singletons somewhere, but in my experience, they're unnecessary in games, and end up being more trouble than they're worth.

In my humble opinion the term "manager" should immediately ring an alarm bell when it comes to designing your application.

QFE.
Usually if you think about the design a bit more, you'll realise you're designing a cache or a factory or a pool, or some other specific structure (not a vague "manager").

Or maybe you're designing something that's all three of those (in violation of SRP), and you can actually split it out into separate reusable parts... which you can then reuse to very easily make a texture cache, a buffer cache, etc, etc.

The one thing I never get about the "manager" hate is that even though people say to decompose the class into multiple classes that take care of the different bits of what the manager does, what it comes down to is that they're changing internals and usually there's always still one "manager" class thats the interface for other classes to do things.

A good example being the "resource cache" like, to me all you're doing is putting sparkly glitter on the fact that the resource cache is a resource manager, you're overbearing the point of what it does externally when in reality you're just saying to decompose it into parts.

Sure reducing coupling is good, maybe you want to add loaders to your resource cache for different resources instead of having to mess with the internals, but to me its all semantics. For the rest of your code the resource cache is going to be a resource manager still and it probably should NOT see the other classes the resource manager is using anyway.

tl;dr: I feel like people use the word "manager" like its a semantic for "bad design" when it reality it isn't. In fact I find it debatable whether the term resource cache really explains the purpose of what it does more than resource manager would to the other parts of the program looking in.

Is it a better name? Maybe, but really, it's just that, a name.
Why?

That is certainly one way to do it, but far from the only way.

Games generally like to handle events and flags.

* You care that you got a "move left" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You care that you got a "fire" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You care that you got a "zoom in" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You care that you got an "object placed" event, you don't care if it came from the keyboard or mouse or script or network or AI.

* You generally don't care if the player prefers WASD or IJKL or arrow keys for a keyboard mapping.

* You generally don't care if the player prefers to reverse the up/down axis for a controller

Large games tend to have multiple input mappings that generate a common set of events or states that are consumed or tested.

The code tests against that common interface and not against a specific keyboard or controller.

It's much better and more flexible, thank you for this sample.

I'm never think about this approach, but where I should put this Input Handler? I should pass Input Handler into every Level, or create new one?

It's much better and more flexible, thank you for this sample.
I'm never think about this approach, but where I should put this Input Handler? I should pass Input Handler into every Level, or create new one?

What he's talking about is not a specific architecture but more the idea of translating the idea of making something happen into an abstract event. Some games use something like an event message system where objects register with each other and "listen" for messages, you could also have something like an input manager that you pass around and basically "translates" things like keypresses or network messages into states like that you should be moving left or up or whatever.

For instance as was pointed out, it is a common event in games where something will happen to an object on the map and multiple systems will take notice of it. You may destroy an enemy, that may raise your score, decrement a counter on an enemy spawner, play a sound, run an animation. All these results can be controlled from different points in code, you may have the object manually call each of these things its self, or indirectly tell another system to, you could also make a messaging system where objects "broadcast" when something happens to them and other objects respond to it.

There's different ways of doing it really.. though, I would keep in mind that making such systems is more involved, if you're making a simple game there is nothing wrong with hardcoding the keys you want to use, or making a very simple keyboard class that tracks the state of each key or the mouse. There's no golden bullet to the question, it depends on your needs and complexity.

An important thing is what I would term "delegation of responsibility" figuring out which part of your game should be in charge of making something happen. Probably the most basic design is to have something like an enemy ship in a 2d scroller tell the other systems it exploded, tell them what to do directly. Simple but not very flexible.


A good example being the "resource cache" like, to me all you're doing is putting sparkly glitter on the fact that the resource cache is a resource manager, you're overbearing the point of what it does externally when in reality you're just saying to decompose it into parts.

You have that a little backwards, methinks. Part of the argument is, as you say, that the manager class is too monolithic and should be refactored into several, more tightly focused classes. But a manager means different things to different people and in different contexts. Like Hodgman said, it could be a cache, a factory, or a pool, or maybe even something else, or some combination of them, depending on what it's managing. Those are all well-defined computer science terms. Cache is the typically used suggested alternative because texture manager tends to be such a popular how-do-I- topic.

I agree that once it's broken down into a system of classes, there is typically one class, or sometimes a small subset of the classes, that act as the interface to the rest of the program. But that interface is more tightly focused, and if designed well, rarely needs to be global. Often the class acting as the public interface may not even be the cache or factory or pool that served as the core of the old manager class.

Ultimately, the manager phenomenon is a manifestation of inexperience and ignorance. It's relatively easy to learn to code self-guided from books and we sites. It's more difficult to learn good design and to think like a programmer without guidance. And so newbies create managers because they haven't gained enough theory to better define what they intend the class to do, and they make them singletons because it looks like an answer to their design problems. And they get away with it for a time, because the new problems are much more subtle. And then they start looking for better ways to do things when they finally stumble. And they get told that managers are bad and global are bad and singletons are bad. And if they're fortunate they're given advice on better design. And if they're really fortunate it's comprehensive and easily followable. That's just how it is in the Internet we live in.

So, some caches may be part of some managers or management systems (or modules or whatever), but I disagree that it's all semantics.

This topic is closed to new replies.

Advertisement