Advertisement

Trouble switching from inheritance to composition.

Started by December 16, 2016 01:41 AM
24 comments, last by Heelp 8 years ago

Hi all,

I just decided to cut down my 1000 year-old inheritance tree because of too many nasty problems you probably already know of.

But I'm trying to draw all the stuff in my head before starting to make such a big architectural change. Big for me, probably not so big for you.

Ok, let's imagine I've rewritten all my classes using composition.

The way I see it, my GameObject will look like this:


class GameObject
{
  Matrix transform;
  Model* model;
  Physics* physics;
  Renderer* renderer; //Or I'll just use a static void render(GameObject&) function instead of a whole class, for starters.
}

Ok, cool. But now I need a Player object.


class Player
{
  Input* input;
  Animator animator; //for skeletal animation
  Matrix transform;
  Model* model;
  Physics* physics;
  Renderer* renderer; //Or I'll just use a static void render(GameObject&) function instead of a whole class, for starters.
}

Imagine 10 more different classes like this.

Final step is to render all entities using a 'for' loop.

If I was using my bad old inheritance tree, I would just do:


for( auto& object : gameObjects ) { Renderer::draw( object, viewCamera, interpolationRatio ); }

And have no problems since every monster inherits from GameObject.

But now that I have 10 classes for 10 different monsters, how do I render them at once? :huh:

1. overloading the Renderer::draw() function doesn't count

2. Stuffing every component in the world in the GameObject and using flags doesn't count. It will become too fat, I'm sure.

3. Entity-component-system is too much. I just want to make a simple game like a normal person.

Heeelp! :o

And thanks for reading. :rolleyes:

Entity-component-system is too much. I just want to make a simple game like a normal person.

There's your problem.

Composition means composing things out of other things. The thing says say "Here is a container with all my things, do the right stuff with them -- they're all composed of interchangeable parts".

The problem is that it tends to look exactly like an ECS system. ECS systems are nearly always implemented in terms of simple composition.

It does not need to be complex and convoluted, but at the core an ECS system is composition.

You don't need to write Unity or Unreal. But if you are composing things to fit inside other things, they all need to implement a common interface. You don't have to call them "components" if you don't want to, but the concept won't change.

Advertisement
Composition also allows for having multiple tiers of component.

So for example you could have a PhysicsObject which composes a Transform, a PhysicsBody, and a Model.

A Character is then a PhysicsObject with some Stats, for example.

A Player is a Character with Input.

And so on.

By bundling commonly shared components, you can make the organization much cleaner, without resorting to inheritance.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

You overcomplicate your problem.

Nothing bad with inheritance as long as you don't redefine the rules.

The way I see designs is implementing an IRenderable interface which has 1 method of Render.

This way you pass to your graphics component all IRenderable objects, which can be a player, a monster or even a gui component.

Because it doesn't care about details- all it wants is something to render.

However, you want something simple- abstractions are not simple, They become really heavy burden to maintain when you split your behaviors into many abstractions.

So in terms of simplicity you can have an abstract class which defines basic methods for a game object (And the point here is to define what's a game object? An entity that is rendered into the game? Does it have physics? Does it store information? That's where your design skills is used). This way your higher level components (Render, Physics, Input, Game [Damaging, score, etc...]) know only of a GameObject.

As for your specific issue: There's a simple guideline to follow, abstract anything that is common. Anything else leave in the specific classes.

These 4:

Matrix transform;
Model* model;
Physics* physics;
Renderer* renderer;

Are common. Then why don't you keep them in a base class?

Don't redefine, Re-use.

Good luck!

Thanks to all for the replies. ^_^

WoopsASword, inheritance is not the way to go, man. Or maybe I'm just missing something, but I doubt it. I would really like to switch to using components.

I don't say inheritance is bad as a tool, it works, but it doesn't work when it becomes a big, fat tree that I'm afraid to inherit from, because I get loads of functionality that I don't want just so I can get 2 functions I need. And I always wonder whether I should copy/paste some code, or inherit, but I don't want multiple inheritance, and yeah...

Guys, I kind of like the idea of putting 100k components into GameObject and then if I want to use something, I just set the flag to true. The GameObject will be giant, but once I get used to it, I will be ok. Bear in mind that I don't have a team, so readability for others is not an issue. What do you think?

What do you think?

I think you should use the tools that are the best suited for what you want to achieve.

Inheritance represents "is-a" relationships. It's perfectly suited for when you want to model inheritance.

Composition represents "has-a" relationships. It's perfectly suited for when you want to model composition.

Swapping everything to composition or swapping everything to inheritance seems like a really bad idea. Use what's appropriate, when it's appropriate.

If your problem is having a "big fat tree" that you're afraid to inherit from, that's a sign that you probably shouldn't inherit from it in that case.

Either because your base object is far too complex, or because the thing you are trying to model does not fit the inheritance model.

If you can guarantee something is common for absolutely everything, put that as the common object and inherit from that.

There are already some excellent answers in this thread, I would suggest you reconsider them instead of dismissing inheritance completely.

EDIT: That is to say... don't swap for the sake of swapping. Swap if and when there is a good reason to swap.

Hello to all my stalkers.

Advertisement

Lactose!, there are many reasons. I just can't code anymore man, my code is a mess. And I'm not productive.

For example, if I have a Character like this


class Character
{
  CollisionBox box;
  Health hp;
  Input* input;
  Animator animator; //for skeletal animation
  Matrix transform;
  Model* model;
  Physics* physics;
  Renderer* renderer; 
}

Imagine my character has the magic ability to become invisible, where he can go through walls and doesn't take hp.

So when the character is invisible, I would just like to remove the CollisionBox and Health out of the equation. With inheritance it's messy and ugly. But with composition, I can just remove the Health and the CollisionBox components when I'm invisible, and add them back when I'm visible, for example.

EDIT: Entity-component-system is shitty, because I don't want to put all of the behaviour in a system and leave the components to be just data, I mean who does that stuff?? <_<

And inheritance is ugly, not flexible or extendable and not readable, there's no argument about that. :angry: Talking about games here, not business stuff.

Need to come up with something else, but needs to be doable in C++, of course. :wacko:

I decided on switching to composition and putting both data and behaviour in the components. And if some data is shared by more than one component, I will declare it in the entity. But I will use shared variables only for the basic stuff, to keep the entity clean. And for the other stuff, I will just send messages between the objects somehow, will figure that out later. ^_^

EDIT: Now I decided to put all possible components in GameObject, and do like this:


  if (self.hasRender) {
    [self updateRender:dt];
  } 
  if (self.hasHealth) {
    [self updateHealth:dt];
  }
  if (self.hasTeam) {
    [self updateTeam:dt];
  }
  if (self.hasGun) {
    [self updateGun:dt];
  }
  if (self.hasMove) {
    [self updateMove:dt];
  }
  if (self.hasDamage) {
    [self updateDamage:dt];
  }
  if (self.hasPlayer) {
    [self updatePlayer:dt];
  }
  if (self.hasAI) {
    [self updateAI:dt];
  }
  if (self.hasHuman) {
    [self updateHuman:dt];
  }

Simple and cool for noobs like me.

Thanks to all for the replies. ^_^

WoopsASword, inheritance is not the way to go, man. Or maybe I'm just missing something, but I doubt it. I would really like to switch to using components.

I don't say inheritance is bad as a tool, it works, but it doesn't work when it becomes a big, fat tree that I'm afraid to inherit from, because I get loads of functionality that I don't want just so I can get 2 functions I need. And I always wonder whether I should copy/paste some code, or inherit, but I don't want multiple inheritance, and yeah...

Guys, I kind of like the idea of putting 100k components into GameObject and then if I want to use something, I just set the flag to true. The GameObject will be giant, but once I get used to it, I will be ok. Bear in mind that I don't have a team, so readability for others is not an issue. What do you think?

I personally prefer a "big fat tree" as long as it's properly designed.

You can't run away from complexity, some code are so complex you have to sit monthes just to understand it.

What can we as developers do? Simplify as much as we can!

Your understanding of what "composition" should consist of is not well.

Composing objects should not interfer with your inheritance.

"100K components into "gameobject"" - This is not how it works.

If not all derived "gameobject" need these 100k objects, than It's not the place for these objects.

"readability for others is not an issue" - There's a special quote for that:

"When I wrote that only me and god knew what it does. Now? only god".

EDIT:

I decided on switching to composition and putting both data and behaviour in the components. And if some data is shared by more than one component, I will declare it in the entity. But I will use shared variables only for the basic stuff, to keep the entity clean. And for the other stuff, I will just send messages between the objects somehow, will figure that out later. ^_^

EDIT: Now I decided to put all possible components in GameObject, and do like this:


  if (self.hasRender) {
    [self updateRender:dt];
  } 
  if (self.hasHealth) {
    [self updateHealth:dt];
  }
  if (self.hasTeam) {
    [self updateTeam:dt];
  }
  if (self.hasGun) {
    [self updateGun:dt];
  }
  if (self.hasMove) {
    [self updateMove:dt];
  }
  if (self.hasDamage) {
    [self updateDamage:dt];
  }
  if (self.hasPlayer) {
    [self updatePlayer:dt];
  }
  if (self.hasAI) {
    [self updateAI:dt];
  }
  if (self.hasHuman) {
    [self updateHuman:dt];
  }

Simple and cool for noobs like me.

This is terrible.

This is terrible.

WoopsASword, I kind of disagree, man. And I will tell you why. My game will consist of maximum 30-40 components. This method may not be suitable for large-scale games, but saves a lot of developing time. And I'm doing a small game. In my opinion, there is no terrible code or pattern, there's just code that is not suited for the purpose.

If you still don't agree with me, you can post and tell me why I'm wrong. I'm open to criticism. ^_^

I think you should use the tools that are the best suited for what you want to achieve.

See, you said it. :cool:

This topic is closed to new replies.

Advertisement