0

So I have built a Telegram bot where you can play a rpg. This is the class structure.

Creature

MobType <-- Creature

Mob <-- MobType <-- Creature

Mob1 <-- Mob <-- MobType <-- Creature

Mob2 <-- Mob <-- MobType <-- Creature

Where:

Creature contains functions shared with User class

MobType contains data from database table, which is equal for every mob of the same type (Name, stats xlvl ecc)

Mob contains data from a database table which differs from mob to mob (current hp, level ecc)

Mob1, Mob2 ecc are class I made for customize each mob, like if a certain mob is immune to fire, when it gets Fire Damage, I can tell this class to ignore it overriding a function.

I explained only mob architecture but others like Skill, Items ecc are equal.

So my question is: Is this architecture good enough? Is this the best? If not why? (of course it is not)

EDIT: I could even share the repository on GitHub if needed. Is it against the rules?

4
  • 2
  • 1
    Mob1 and Mob2 are about as bad as you could ever hope to get when it comes to class names. I hope these are just examples and not the real names in your code.
    – David Arno
    Apr 4, 2019 at 10:48
  • Consider reading Head First Design Patterns The first chapter is entirely about a very similar problem, and it builds up a clear detailed example of what all the (current) answers are saying.
    – user214290
    Apr 4, 2019 at 11:30
  • @DavidArno unluckily those are the real classes names. I called them like that in order to be able to instantiate them dinamically. The number is the MobType id
    – Donny
    Apr 4, 2019 at 11:48

2 Answers 2

3

No. Inheritance trees like this tend to fall down. Particularly in games.

Your are more likely to get up with more technical objects such as:

AnimatedCharacter <-- 2dSprite

--EDIT 09:55 UTC, rolling news updates begin

So what you are attempting to do is codify your business rules, or in the case of a game your 'game logic' into an inheritance tree. To expand on your example..

Dragon : Mob
{
    override public void takeDamage(Damage damage)
    {
        if(damage.Type == Fire) { .. do nothing..}
    }  
}

The problem with this is that

It keeps expanding. So say I invent a new Mob, Salamander

FireResistantMob : Mob
Dragon : FireResistantMob 
Salamander : FireResistantMob

But it's inflexible. Say I have a new creature that takes no fire or ice damage

RockGolem : FireResistantMob  : IceResistantMob //does not compile!!!

Small changes have large and unexpected effects. Say I change fire Damage so that it burns inventory items. But now all my fire resistant creatures' inventory doesnt burn as expected.

Consider that Mob is an abbreviation of "Mobile" the functions we need to share and propagate are not the ones related to game rules, damage, types of monster etc, they are the ones about what moves from location to location. How to display things on the screen, what things we can click on etc.

A single Mobile class with variables can account for all possible monster types. eg:

public class Mobile : LevelContent
{
    Dictionary<DamageType, double> damageResistances;
}
3
  • What do you mean by falling down? How could I implement overriding function like in the Mob1, Mob2 ecc then?
    – Donny
    Apr 4, 2019 at 9:49
  • 2
    Use composition. If you fill in the details you will see that overriding is neither required or useful. The inheritance tree becomes and obstacle to new features rather than a helpful construct.
    – Ewan
    Apr 4, 2019 at 9:51
  • Please, could you edit your answer adding how would you do the Mob class with composition? Just pseudo-code to understand don't need to be detailed you know, thanks
    – Donny
    Apr 4, 2019 at 9:54
1

Prefer composition over inheritance (see this Stack Overflow question). Creating endless inherited sub-classes just to add traits to objects results in a huge messy class tree. It doesn't handle objects having multiple traits well (you have to create even more classes), and it is even harder to have modifiable traits.

It is usually better to have a simple set of classes, with each possessing a list/set/other container, to which you can add or remove extra traits.

2
  • I have something like this 'composition' with equips, Mob class has a variable in which an array of Equips object are stored. This is composition, right? But then how do I do that with Mob1 and Mob2?
    – Donny
    Apr 4, 2019 at 10:06
  • @Stever Use only one class, add another list to it that holds all special abilities. In the constructor, populate that list according to the type of thing you're creating. The nice thing about that is that the abilities can be constant or variable, as you wish.
    – Simon B
    Apr 4, 2019 at 12:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.