The Manager (Anti)pattern
There is nothing wrong with a class whose purpose is to manage a collection of objects. For example, a database connection pool is a valid "DbConnectionManager", since it can improve application responsiveness while removing a lot of overhead from developers. And in some sense, a List<T>
is a manager of objects of type T
, where the objects don't require a lot of supervision.
However, the Manager
(anti)pattern often gets a bad name for a number of reasons.
First, there's the issue of naming. Often, by taking the shortcut of tacking the word "Manager" onto a class name, we lose the opportunity to clearly describe the purpose of the new class. If I wanted to track the inventory of vehicles on an asphalt plane, I would much rather see a ParkingLot
than a CarManager
. The former much more clearly communicates the intended usage of the class, and offers other benefits that I describe below.
Second, managers tend to grow exponentially as the project grows. This is true for businesses and for the codebase itself. Once you start calling everything a Manager
, you'll find it hard to stop. Before long, you'll need a ManagerManager
to keep all of your managers in line.
By giving descriptive names from the start, and not defaulting to a generic name, we greatly weaken the stranglehold that managers have on our project. Well-named classes don't completely remove the possibility for an explosion of manager classes, but they can certainly help.
Third, it's much easier to adhere to the Single Responsibility Principle if we use descriptive names. Both a Dealer
and a ParkingLot
are responsible for managing vehicles, but I don't expect a parking lot to be selling me anything. We'll have more classes, but each of them will be more limited in scope and usage. Unfortunately, nothing's stopping a developer from throwing all that functionality and the kitchen sink into a CarManager
. For this reason, we should avoid names that lend themselves to poor design practices.
Can we manage without a manager?
Get a Manager
With all of that being said, don't forget the first thing that I said:
There is nothing wrong with a class whose purpose is to manage a collection of other classes.
Whether you call it a "manager" or something else, you may still need something to keep track of your tasks. Do you need to query the status of tasks and display them on a UI, print to console, etc.? Then you need something to keep track of the list of available tasks. Again, this can be a simple List<Task>
if that's what works for you.
Or Don't
On the other hand, sometimes we can get away with not actually having a single manager object keep track of a collection of tasks. Rather, we can let task manage its own state. This is often accomplished by means of events or method callbacks, and is useful in asynchronous programming when we don't when a task will end.
For example, in C# I can define events such as TaskStarted
, TaskFailed
, and subscribe to these events when the object is first created (or whenever I have a reference to the object, but this is harder to keep track of without a list of some sort). Example:
public class Task
{
event EventHandler TaskStarted;
event EventHandler TaskEnded;
}
// And in the wild
public void DoStartTask(object sender, EventArgs e)
{
// Do stuff
}
public void DoEndTask(object sender, EventArgs e)
{
// Remove references to event for garbage collection
((Task)sender).TaskStarted -= DoStartTask;
((Task)sender).TaskEnded -= DoEndTask;
// Do other stuff
}
Task myTask = new Task();
myTask.TaskStarted += DoStartTask;
myTask.TaskEnded += DoEndTask;
// Even if I don't add it to a list, the DoStartTask and DoEndTask will fire at the right time.
Class self-management of this type works best when the objects are truly self-managed. If you need to query a list of tasks, e.g. retrieve them from a datasource, you will probably have a better time with a single authority (i.e. a manager) than multiple individual self-managed tasks.