How should i call my classes instead of "xyManager"?

Started by
9 comments, last by Irlan Robson 9 years, 4 months ago

Hello!

I am currently working in an RTS game in C++.

I read in alot of place that calling something "Manager" is generally bad, because it's too generic and say nothing about what the class really does.

However, i have no idea how to call them instead. Currently i have these:

- ImageManager: It load the images, and every class that need a texture can get it from this class. This is because IO operations are REALLY expensives, so i rather read the images once and use all from the memory.

- Building and Unit Manager: These classes have a container, which contains all Building/Unit which are currently alive. You can update them through this class and it's also check if you clicked to one of them.

- EventManager: I didn't wrote it yet, but this supposed to handle Commands and other events. You can basically post any command or event to execute them in the next iteration of event loop.

Any idea what name would fit better what these classes do?

Thank you very much for the answer!

Advertisement

The manager name comes from putting too much global functionality into a single class. Its a good idea to follow the Single Responsibility Principle and have each class only be responsible for one job.

Just to give an example, in extreme case you could split the ImageManager into TextureCache, TextureUploader, Texture, ImageCache, ImageDecoder, Image and reusable FileCache, FileLoader, MemoryBuffer and MemoryAllocators, though most likely this is way too much.

Building and UnitManager seem to have no clear purpose, you could just use some reusable templated container class and add functions in the relevant subsystems doing work on the whole container.

EventManager I would skip, too. A Queue without any special functionality (besides thread safety if needed), where you put in Events, will do for a while.

The OP is somewhat vague, so I write down some possible pitfalls I see:

Your ImageManager may be problematic since it violates the single responsibility principle if implemented as a single class. The single tasks possibly involved here are (a) allocating local storage, (b) reading a resource from mass storage, (c) uploading the resource, (d) implementing a cache strategy, (e) granting access. Okay, all this together means to manage the resource, but the tasks are too different to be implemented in a single class. Instead, if you understand the manager as a facade granting a small API in front of the wrapped functionality, it is IMHO okay to name it "manager" if you wish.

Your BuildingManager and UnitManager are even more problematic. They implement some kind of scene setting but also implement collision detection and update services!? These tasks are not related and IMHO can not even be subsumed in a single "management".

It reads like your EventManager can be named EventProcessor. However, if it executes commands that are generated by events, it is perhaps a CommandProcessor supplied by an EventDispatcher? Because "handle Commands and other events" doesn't seem me to fit in a single class.

EDIT: wintertime was a bit faster than me, and I second those post :)

Your ImageManager may be problematic since it violates the single responsibility principle if implemented as a single class. The single tasks possibly involved here are (a) allocating local storage, (b) reading a resource from mass storage, (c) uploading the resource, (d) implementing a cache strategy, (e) granting access. Okay, all this together means to manage the resource, but the tasks are too different to be implemented in a single class. Instead, if you understand the manager as a facade granting a small API in front of the wrapped functionality, it is IMHO okay to name it "manager" if you wish.

I try to be a bit more accurate, there is not that much functionality in the ImageManager.

It has an Init function, which load the textures to a container, and have a function like that: sf::Texture getTileTexture (Image::TileTexture texture);

Which give back the given texture (TileTexture is an enum)

So it does 2 things: Load the textures, and give an interface to get the texture you need.


Your BuildingManager and UnitManager are even more problematic. They implement some kind of scene setting but also implement collision detection and update services!? These tasks are not related and IMHO can not even be subsumed in a single "management".

No, these classes just iterate through all Units and Buildings, and call the update on them. Everything else is done by the given Unit's or Building's update function.


- ImageManager: It load the images,
ImageLoader then, or ImageCache. Or an ImageCache in which you plug an ImageLoader. Caching strategy doesn't depends on the format of the image IMO so you could plug different ImageLoaders for different formats and the ImageCache could store the data loaded by them. Then if you don't want a cache for certain things, just use the loader directly.


No, these classes just iterate through all Units and Buildings, and call the update on them. Everything else is done by the given Unit's or Building's update function.
Then that "update" function should do less things.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

ImageLoader then, or ImageCache. Or an ImageCache in which you plug an ImageLoader. Caching strategy doesn't depends on the format of the image IMO so you could plug different ImageLoaders for different formats and the ImageCache could store the data loaded by them. Then if you don't want a cache for certain things, just use the loader directly.

ImageCache sounds fine, thanks.

Then that "update" function should do less things.

Well, it updates the objects with the elapsed time. Yes, this can mean alot of things, like train units, move, attack and other stuffs, but i feel like it's reasonable.

I try to be a bit more accurate, there is not that much functionality in the ImageManager.
It has an Init function, which load the textures to a container, and have a function like that: sf::Texture getTileTexture (Image::TileTexture texture);
Which give back the given texture (TileTexture is an enum)
So it does 2 things: Load the textures, and give an interface to get the texture you need.

You didn't tell you were using a library like SFML, which is doing most of the work already. Internally SFML has a number of classes already interacting to provide that functionality, including Texture, Image, some loader wrapper and stb_image doing actual loading and decoding.
You may not only want to give back the texture (and its important to use a reference, do not return it by value), but also give back an sf:Rectangle for the texture coordinates of a tile when one texture contains a whole tilemap. Maybe call it something like TileTextureFinder.

No, these classes just iterate through all Units and Buildings, and call the update on them. Everything else is done by the given Unit's or Building's update function.

There was a recent discussion on another thread that you should not have a single loop calling a magic update, as that can cause inconsistencies from objects accessing old state from one object and new state from another. Have separate functions doing input, movement, drawing and so on, then add a loop for each kind of functionality.


ImageCache sounds fine, thanks.

Well ... notice the process: A client requests the resource library (to avoid the word "manager" here ;)) to return a specific resource. The library requests the cache for the resource. The cache does not store it, so returns nil. The library decides which storage allocator to use, and triggers the loader to load the resource by utilizing the storage allocator. The loader investigates its directory structure (keywords are archive file and file overriding) and detects the file. It looks for a suitable file format, requests an importer from it, and calls it to read the resource. The resource is then returned to the library. The library looks which cache policy is installed for that resource, and puts the resource into the belonging cache. It then returns to the calling client.

From such a description where the single tasks are sectored, one can see how many instances are actually involved in such a "simple" thing like resource management. Many of them can be agnostic of the actual resource type, some can (or should) not. Almost all of them need not know how the resource is encoded on mass storage. Many aspects can be configured (like the storage allocation policy, the cache policy) or are somewhat runtime dependent (like the file format) or are not relevant to the client (like is the resource cached or not). Implementing all this into a single class is wrong, as well as exposing it into the wild is wrong (*), regardless of how the class is named.

You have already mentioned that your process is not that comprehensive, and that you rely on 3rd party libraries. However, the above should demonstrate how a comprehensive solution may look like in detail, especially how many classes are involved in dependence on the inherent tasks.

(*) This is why I would not name the facade class "ImageCache".

Lots of good advice already.

I read in alot of place that calling something "Manager" is generally bad, because it's too generic and say nothing about what the class really does.


Many (probably even most) games are filled with *Manager classes. It's just a name.

ImageManager could be split into ImageCache and ImageUploader, sure, but those could be called ImageCacheManager and ImageStreamManager. 'Manager' is just a name that has no real meaning on its own. That's the (potential) problem with 'ImageManager' - it could as well have been called 'ImageSystem' or 'ImageEmperor' or 'ImageThingamajig'. Unless you have a clear and well-defined meaning of what 'Manager' means in your code (which you very well might!) then you can end up with *Manager classes filled with too much different stuff.

You can totally follow SRP and still use whatever naming you want. A game might have a BulletManager that isn't doing a bazillion different things but is just ensuring that bullets (which some games can have thousands of on screen at a given time) are efficiently managed. It might just be glue out to the graphics and physics and game logic systems that makes sure they all stay in sync without needing a full fat game object for each individual little short-lived bullet. Totally reasonable.

Or your ImageManager might just be an aggregate of ImageCache, ImageUploader, and any other Image* classes that manage things.

My point is: don't get stuck on what are honestly utterly irrelevant design decisions like whether you called a class a FooManager or not. What you name your classes does not make a better game. It does not automatically make your code any better or more maintainable. There's good advice to follow regarding the single-responsibility principle, well-documented code, and consistent names, but not a single one of those are at odds with any particular way of naming classes. Beware that you can easily fall into a trap of renaming half your codebase every month (and then never get any useful work done) because some armchair engineer on the Internet wrote a convincing blog post about why you should do something his way instead of some other way.

And again, the others' advice is all good. I just wanted to point out the trap I see you potentially walking into here. smile.png

Sean Middleditch – Game Systems Engineer – Join my team!

Not 100% sure if I read it correctly, but you could have something like this in your case:

Class object
Class building (derived from object)
Class world or level, having a vector of buildings etc

Class material (includes texture etc)
Helper class including texture loading function etc.

Class renderer, which can take a world (or to be exact the parts of the world to be rendered).

The world->load function can load everything, the update function can call update functions for its members. Then your world would contain x materials and buildings, which have a reference/ id to the world (or level's) material. This way you're still reusing textures buth with a more clear/ less 'GOD class' hierarchy.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement