How deep should Item subclass inheritance be?

Started by
11 comments, last by tufflax 10 years, 4 months ago

I am using c++, and right now I am just working on the inventory and items system. Also none of the names are actually used, just meant for easy representation. My question is this: given the base class Item, how deep and/or broad should the subclass inheritance go? For example, one of the types of items in my game are usable items such as potions, cures for status ailments, and so on. Should I create a subclass called something like UsableItem and then create subclasses for each type of item or just create each type of item and make them sublcasses to Item?

Visual representation:

Item --> UsableItem --> Potion OR Item --> Potion

--> Cure --> Cure

-->Other --> Other

As far as specific item classes go, like Cure, is it possible to use Cure to create multiple different Cure objects where each one can handle different types of ailments?

Cure class --> Cure object for poison

--> Cure object for burn

Advertisement

You should use composition rather than inheritance. IMHO for games in particular inheritance deeper than one level (where it can come useful in some cases) is questionable.

Think about it - what if a potion could do multiple things? Inheritance becomes a tangled mess of relationships, whereas you could instead have a Potion class, which takes a list of Attributes which give a certain effect to the Player drinking them (say, +20 health, +50 strength for some time, cure all poisons, ..) and those Attributes could themselves be composed of smaller attributes (e.g. "cure" and "cure target: burn") in order to allow very complex effects while keeping the logic small and self-contained.

If you want to go full entity-component-system, then you don't even need the Potion class. You can just have an entity with components "name" (Potion of ...), "drinkable" (for instance), and then a bunch of Attributes that will be applied when drinking. But that may be overkill, or might not map well to the particular gameplay you have in mind.

Think about the first example, and see if you understand it, see if you can draw the benefits of using that approach. It will likely make your code much easier to reason about than using brute force inheritance.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

Prefer composition (i.e. have members which produce effects) rather than subclassing. Inheritance should only represent an is-a relationship.

E.g. we have a window class, and we have a resizable window which is a subclass. We also have a window with a horizontal scroll bar, but we can resize that? ResizableWindowWithHorizontalScrollBar? Now we want vertical scroll bars too. ResizableWindowWithHorizontalAndVerticalScrollBar?

Or use composition?

Another example: Vehicle class, and we have Car and Boat as subclasses. Now we want a Hovercraft (which incidentally, has a vector<Eel>).

Inheritance is best used if you want polymorphic behaviour via virtual functions and can treat any derived class as a base class at some point.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley
Shallow, wide inheritance trees are generally good. You might have one hundred direct children of 'Component' or 'GameObject'.

Deep, narrow inheritance trees are generally a symptom of bad design.

A potion is probably a game object. It probably has several components.

So, it would be the same as doing something like a Weapon class that would have ranged and melee subclasses, but those subclasses could be used to make all the ranged and melee weapon objects that could possibly exist (not at the same time obviously) in a particular game?

I remember hearing the number 5. That if your class hierarchy exceeded 5 levels you should look at that as a code smell.

Obviously thats just a rule of thumb, but seems like a reasonable one.

Does inheriting from UsableItem provide any functionality or are you using it for classification only? If classification then there are other ways to do that. If it provides functionality then sure.

Component design can be really nice, but note you end up building your objects at run-time vs design-time. It can be a different way of thinking about it and once you go down the route you can get lost and very abstract and interactions between components can get interesting.

I had a component system where the components had events that they would fire and functions to do functionality. At run-time you would assign the components to game objects and hook up the events to functions between different components. This loosely couples all classes you write where they don't need to really know much about each other, especially if you keep the parameters very primitive in your events/functions.

Consider lifting common uses of vaguely related classes into interfaces instead of using class hierarchies.

A typical potion is IConsumable but not IEquippable.
A typical sword is IEquippable but not IConsumable.

A unique amulet which normally gives you a small defense bonus, that you can optionally sacrifice to become invulnerable for 30 seconds could be IEquippable AND IConsumable.

So prefer inheritance hierarchies like I prefer my women, wide and shallow?

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

You should use composition rather than inheritance. IMHO for games in particular inheritance deeper than one level (where it can come useful in some cases) is questionable.

Think about it - what if a potion could do multiple things? Inheritance becomes a tangled mess of relationships, whereas you could instead have a Potion class, which takes a list of Attributes which give a certain effect to the Player drinking them (say, +20 health, +50 strength for some time, cure all poisons, ..) and those Attributes could themselves be composed of smaller attributes (e.g. "cure" and "cure target: burn") in order to allow very complex effects while keeping the logic small and self-contained.

If you want to go full entity-component-system, then you don't even need the Potion class. You can just have an entity with components "name" (Potion of ...), "drinkable" (for instance), and then a bunch of Attributes that will be applied when drinking. But that may be overkill, or might not map well to the particular gameplay you have in mind.

Think about the first example, and see if you understand it, see if you can draw the benefits of using that approach. It will likely make your code much easier to reason about than using brute force inheritance.

I actually kind of like the full entity-component-system idea, although it would probably get really complicated at some point like you said. However, it does bring up one issue that I wish I had thought about before hand (that doesn't necessarily deal with the full ecs): When the player does something that would result in finding an item in the world (opening a chest, killing something, resource gathering as some basic examples) and the item object is created, how exactly is the information for the specific item obtained?

For example, when my friend made a small text-based game in Java he made a static class that contained an array of item objects and just copied the item needed into the instance found. Probably a horrible idea for a bigger project like mine but it kinda shows what I'm getting at.

For my project, I would do something like:

Item item1 = new Item(components...);

where the components are things like name, sell value, isSellable, etc. In a lot of cases, I want the items that are found in many cases to be random. So when the random item is determined where should the information be pulled from? A txt file?

This topic is closed to new replies.

Advertisement