Having trouble harnessing the power of Inheritance. (best practices advice requested)

Started by
19 comments, last by WozNZ 9 years, 1 month ago

People talking about the component system are actually right in the long run, all depends on the complexity of your object, operations, game though. For a simple game without much permutation then inheritance is probably fine.

All the classical books on OO use stuff like a shape drawing app etc where you have Shape and inherit to create Rectangle, Triangle etc.

Inheritance will bite you hard down the line though as others have explained. Composition > Inheritance and using interfaces instead of base classes (unless pure abstract) also gives you far more flexibility and eases testing via mocks etc.

As others have pointed out your tests for null should not always be required. For components that you expect to always exist then why test them, if they are a null reference that is actually an invalid state and hence breaks whatever contracts you think your components should meet. If you expect your component to be constructed with specific components us something like a debug assert as you pass it in at construction time to catch when this is not the case.

Debug.Assert(comp != null)

The other way to handle the composition if you think creating all the component classes is too much is to steal tricks from the functional world and use function pointers to minimise the component class scaffolding, depending on the complexity of your operations.

public class Baddie

{

private readonly [Action|Func]<?...?> _baddieMover;

public Baddie([Action|Func]<?...?> baddieMover)

{

_baddieMover = baddieMover;

}

public void Move()

{

_baddieMover(params here);

}

}

The factory that creates your components injects the operations at construction time much like the component system.

Action<int, int> = void Name(int param1, int param2);

Func<int, int> = int Name(int param);

Then you just need a class with a set of static functions for the different move type operations and just plug in the one required. You can also get into tricks like partial application if a function is not quite the right shape for your need via lambdas

Given

void MoveNpc(Point location, Point vector, int speed)

you can reshape to a

void MoveNpc(Point location, Point vector)

via

Action<point, Point> MoveWalkingSpeed = (location, vector) => MoveNpc(location, vector, WalkingSpeedConstant);

This also gives you access to the closure the lambda is created in to inject all manner of extra funkiness if required

But this is all pushing you in yet another direction.

Small simple stuff then Inheritance is useable

Complex stuff with many permutations then composition is the only true path.

This topic is closed to new replies.

Advertisement