Argument against "manager" classes

Started by
52 comments, last by Zahlman 17 years, 4 months ago
I know that usually inexperienced individuals do this, but I have even books that have "manager" written in sample code. I think that this needs to stop for the following reasons: 1) Most "manager" classes are just niave encapsulations (or worse, replacements) of standard STL containers. If you are keeping objects in a std::list then just define an std::list, 90% of the time you wil not benefit from some custom interface. STL containers are amazingly versatile things especially when combined with STL algorithms. The end user should not be robbed of the versatility of STL containers. 2) "Manager" is often used to describe classes that do a wide range of very different things. Some handle garbage collection, reference counting, memory allocation, thread synchronization, buffer creation and reuse, inter object communication. 3) Most "manager" classes can be more effectively replaced with the following solutions: - a) A simple raw STL container with 0 encapsulation - b) A version of the "mediator" pattern (look up Design Patterns) - c) A version of the "factory" pattern (look up Desing Patterns). There is no reason why a Factory cannot contain data that is shared between instances of the created object. - d) This is an idea that I am modifying, but... A "resource pool" pattern. I consider a "factory" that maintains a list of objects for reuse of those objects by multiple users as a "resource pool." There are many types of "pools" demonstrated by programmers far better than me. I have seen VBO pools, streaming buffer pools, thread pools, etc... - d continued) Essentially, anytime that you have an object who's creation and destruction is costly there is a chance that a pool is the solution. Instead of destroying the object you keep it in memory and reassign it to another user when it is needed again. It is also a good solution for when the same data is used by multiple object instances (ie, a mesh used by several different instances of an Entity). When the object is loaded into memory you can hold on to a copy of it, pass out a const (which is an obvious limitation) reference so that it is not copied, and divy out references everytime that same data is requested. Regardless of what specific solution that your application needs the fact still remains that the term "manager" will provide zero hint as to what you are actually describing. Programming is a very exacting science, even in the midst of abstractions, if you are not clear on what it is that is being designed, then it will generate ambiguity. Ambiguity leads to buggy, sloppy, unmaintainable software. If you get the inclining to call something a "manager" please stop first, think about it, and then try to understand what it is that you really need. Chances are that you will come up with a better performing, more versatile, and less ambiguous solution. There are many options out there, please consider them. Also as a side but still slightly related rant... Please stop using "char *" as an ID for everything. Every engine I see does this, and many "manager" classes do this as well. Numbers are faster, std::string's are less error prone, and everytime you rely on abritrary strings as ID's you force every other part of you engine to adapt to that dependance. Maybe this is what you want, but the chances are that there are other solutions that would allow for much looser coupling. Yes... Loose Coupling is a good thing!
Programming since 1995.
Advertisement
Personally, I've always considered a manager as an object that tracks a collection (or collections) of objects and performs actions on the set of objects or a subset thereof. For example: A RenderManager class holds a collection of references to all objects in the world. It stores them in an octree (or whatever structure you want). Calling "RenderManager::Render (target)" for example would then render all the visible objects to the specified target.

To sum up then, a manager is a collection with added functionality common to all contained objects.

As for object IDs - use GUIDs rather than ints. GUIDs are guaranteed to be unique (and DevStudio has a tool to create them). I would personally tie them to a char */string type in debug builds as well just to make debugging easier (using an ID class).

Skizz
Quote:Original post by T1Oracle
3) Most "manager" classes can be more effectively replaced with the following solutions:
- c) A version of the "factory" pattern (look up Desing Patterns).

Ok well, this is true for most of my 'Manager'-classes: they are factories with some extra sugar (keeping track of used memory etc).
What I'm not sure if I understand correctly, have you just written a 500+-word rant to persuade me to call my TextureManager a TextureFactory? [wink]
Sorry but managers are great. I do think sometimes they are over-used, however for resource management they are an excelent choice.

1 - Managers provide functions to edit items WITHIN a list. Just having some STL global container does not provide the same functionality as a manager so you can't really compare them. A manager provides a nice way to organise and expose functions that edit/insert/delete items that its holding. If you don't have a manger and just provide a global array/list/vector then your going to have to keep re-writing the code to edit the list all over the place. Hence a manager - it keeps all the functions dealing with the list in a nice centralised place. And it makes the code reusable as its easy to remove that class and insert it into your next project (while its not easy to dig through all your code that changes a list had you just provided a global link).

2 - So you don't like that manager is a widely used name? How does that make them less valualbe?

3 - I would say the half the things you mentioned in that list ARE managers. You already stated that the term manager covers a wide range of topics, then you say that managers could be replaced with some very specific things - aren't you just listing what a good manager should do?


I'm still not really clear why you hate managers? Infact I don't even really know what you think managers are. Personally I've used managers many times in both my homebrew and profeshinal games and when used correctly they are great. I'd never start a project without cut+pasting my texture/mesh/sound managers in first - they just make everything so much easier and never once have they even shown up on my profiler. Personally mine are fast, they are very reusable, and they are easy to maintain because all the code is packaged nicely into on single class. Whats not to like?
I appologize if any of this sounds mean, my intent is not malice.

Quote:Original post by Skizz
tracks a collection (or collections) of objects and performs actions

Key phrase "peforms actions," those actions are very important. In fact the most important detail of a class to the user is not what it contains but what it does with it. "Performs actions" is a very non-descript (read "ambiguous) way to cause trouble.
Quote:Original post by Skizz
It stores them in an octree (or whatever structure you want)

Awesome, and now your "RenderManager" is coupled to collision detection and/or view frustrum culling.

Furthermore, think about the phrase "whatever structure you want." So, what again is a "RenderManger"? Oh yea a "RenderManger" is "whatever structure you want."


Quote:Original post by Skizz
As for object IDs - use GUIDs rather than ints. GUIDs are guaranteed to be unique (and DevStudio has a tool to create them

Why can't an int be a GUID?
typedef int GUID;class GUIDFactory{public:   // guaranteed to return a globally unique id per instance of GUIDFactory   GUID GetGUID() { return oldID++; };private:   GUID oldID;};


I believe that the term "manager" is a temptation that lures in and then narrows the minds of programmers. If you understand your problem thoroughly then you will that what you are doing can be described in specific terms, and you will likely see that there are more optimal ways to do those things. There is no one "blanket" answer. For an "optimal" (even a somewhat optimal) solution, each problem must be dealt with in terms of its unique circumstances.
Programming since 1995.
Quote:Original post by T1Oracle
I believe that the term "manager" is a temptation that lures in and then narrows the minds of programmers.


lol - now we get to your real argument. You just don't like the term manager. The fact the term doesn't have a very finite definition in no way "narrows the minds of programmers". The fact that they are illdefined is actually a benifit in my mind. I hate people who just stick to design patterns for the sake of it as if somehow that'll make them write good code. Code that has a fancy name is not better code (infact in my experience is usually worse). You can write a manager todo whatever you want, however you want and its still a manager. That doesn't make managers bad - that makes them awsome :P
Quote:Original post by kaysik
1 - Managers provide functions to edit items WITHIN a list.

So does the STL. Ever heard of remove_if, copy_if, transform, for_each? The STL has plenty of useful algorithms. If your application really needs encapsulation on this level then you are most likely in need of a flyweight pattern or maybe a composite.


Quote:Original post by kaysik
2 - So you don't like that manager is a widely used name? How does that make them less valualbe?

A name is a brief yet meaningful summary of what an object does.

Quote:Original post by kaysik
I would say the half the things you mentioned in that list ARE managers. You already stated that the term manager covers a wide range of topics, then you say that managers could be replaced with some very specific things - aren't you just listing what a good manager should do?

Then how does it make any less sense to define the entire application as a manager. After all, your program (game, app, whatever...) only does what a good manager should do. It encapsulates it interal data and performs actions on them What simpler definition of a program could there possibly be?

Quote:Original post by kaysik
never once have they even shown up on my profiler

Did you compare them against any other solutions? What about versatility, loose coupling, and code size? How much of those "managers" aren't even being used? How much code are you writting just to deal with the specific querks of a given "manager?" How much work would you have to do to add in a feature not supported by that "manager?" How much refactoring would be needed to swap one "manager" for another?

When you get the term "manager" what exactly are you guaranteed?

Programming since 1995.
Quote:Original post by kaysik
Code that has a fancy name is not better code (infact in my experience is usually worse)

It is not a name, it is a gaurantee of specific functionality. It is a contract between the user and the library that is partially communicated by the name itself.

It is not about design patterns either. Those are examples of a more specific option in place of the "term" manager.

The saddest thing about "managers" is that they almost always do more than they should and require more of the user than they should.

If the programmer understood their problem domain they would not impose unnessary limitations on the user.
Programming since 1995.
class AClassWithAID{public:    AClassWithAID():    ID((int)this)    {    }    int ID() { return mID }private:    int ID;}


Now every ID is garuanteed to be unique :-)
See also: Don't name classes Object, Manager, Handler, Data.

Personally I hate 'manager' in class names, for a few reasons.

1. FooManager is an invitation for other programmers to lump in anything even slightly foo-related into the same class. Before long you've got a massive behemoth of a class that's doing everything and anything.

2. The use of 'manager' in the name implies one of two things - either the class should be better named (eg. FooFactory, FooPool, FooList), or that theres really two or more classes being lumped into one. Case in point:

Quote:Ok well, this is true for most of my 'Manager'-classes: they are factories with some extra sugar (keeping track of used memory etc).

What you probably should have here is a FooFactory and a FooMemoryTracker instead of a single FooManager.
- You maintain single-responsibility princible for classes.
- Code functionality and data is more localised (less to go wrong, more robust).
- Better code reuse, you could probably have lots of different factories but a single tracker class that works with them all.

This topic is closed to new replies.

Advertisement