I've been refactoring one of my projects recently and ran into a decision I have to make.
I have several interfaces:
Entity
: Something in the game worldActor
: AnEntity
that can perform actionsCharacter
: AnActor
that has a name (as well as some other features I haven't determined yetPlayer
: ACharacter
that is played by a human being.
The type hierarchy is currently
public interface Entity { }
public interface Actor extends Entity { }
public interface Character extends Actor { }
public interface Player extends Character { }
The reason I have it designed this way is because a big portion of functionality will come from community-driven mods that add functionality to the game, and I want it to be as modular as possible.
Now, the decision I ran into is: Should I keep this type hierarchy, or, to allow for more robust design (possibly, I can't think of an example), implement each individually?
The current implementation details are
public class BaseEntity implements Entity { }
public class BaseActor extends BaseEntity implements Actor { }
public class BaseCharacter extends BaseActor implements Character { }
public class BasePlayer extends BaseCharacter implements Player { }
My question is, would there be any benefit (from a design perspective) of doing something like this instead
public interface Entity { }
public interface Actor { }
public interface Character { }
public interface Player { }
public class BaseEntity implements Entity { }
public class BaseActor implements Entity, Actor { }
public class BaseCharacter implements Entity, Actor, Character { }
public class BasePlayer implements Entity, Actor, Character, Player { }
I can't think of a good reason to do this, but someone who would use my API to make their own mod may have their own
public class FooPlayer implements Player, Foo { }
And not want/care about the methods defined in Entity
, Actor
, or Character
.
Best Answer
The decision to make an interface extend another should be based on the Liskov Substitution Principle. So
Actor
should only extendEntity
if every program that's correct when given anEntity
will also be correct when given anActor
. If it's not always the case that anActor
can implement everything anEntity
should implement, then keep them separate. See also the Interface Segregation Principle.If you decide to keep the interfaces separate, you'll need to use generics whenever your methods need an argument that implements multiple interfaces. E.g.
The only cost here is the verbosity of the signature, but otherwise it's not any more difficult to implement or use:
In the case of seperate interfaces I would also recommend choosing composition and delegation over inheritance as illustrated in Thomas Junk's answer. Inheritance hierarchies are brittle and rigid; using composition and delegation it's easy to mix and match implementations of the different interfaces.