3

Imagine that I am writing a game where tanks fight with each other.

A generic Tank class is created and has the method fire() which fires a cannon, looks like this Tank::fire() { /* fires a cannon */ }

And then there is a class for BossTank which fire() fires a cannon and also a missile.


A senior advised me to do something like:

Tank::fire() { fireProjectile(); }, and create a new function:

Tank::fireProjectile() { /* fire a cannon */ }

Then in BossTank we can do inherit:

BossTank::fireProjectile() {
  parent::fireProjectile();
  /* fire a missile */
}

I don't know why, I do not feel good about this. Maybe because the line parent::fire() looks inconsistent with more elementary lines for /* fire a missile */

Another coworker suggested this:

Tank::fire() { fireCannon(); } and Tank::fireCannon() { /* fire a cannon */ }

Then BossTank::fire() { fireCannon(); fireMissile(); } with BossTank::fireMissile { /* fire a missile */ } (this is a method only in this class)


I would like to go along these lines:

Tank::fire() { fireCannon(); fireAdditionalProjectiles(); } with Tank::fireAdditionalProjectiles() empty.

Then in BossTank we can override fireAdditionalProjectiles() to fire missiles.


So here are 3 ways to implement the "same" thing:

  1. Senior: Inheritance - extracting fire cannon so no duplication.
  2. Coworker: Override fire() and implement a new function only in BossTank
  3. Me: Implement new function fireCannon() for no duplication, and fireAdditionalProjectiles() for overriding in BossTank

Is this a personal preference or there are standards regarding this situation?

Sunny Pun
  • 139
  • 4
  • This is still firing from the tank, but a tank HAVE waepon. Just need to think about some aggregation. This has the advantage to not need to inherit each time you need to define a new type of tank. See decorator pattern to add weapons to a tank easily. – AxelH Oct 21 '17 at 21:05

1 Answers1

8

If you have a variety of tanks with different weapons, then it makes sense of compose your tank class

class Tank
{
    List<Weapon> weapons;
    void Fire()
    {
        foreach(var w in weapons) {w.Fire();}
    }
}

If all Tanks always have a Cannon, which always fires then overriding the Fire method and then calling the base class Fire plus some extras makes sense.

But I believe the current trend is towards composition, as people find that with multiple layers of inheritance the logic becomes too hard to follow.

For example, if you have a SuperBossTank which fires two missiles but no cannon you would have to introduce a new BaseTank with no weapon

Ewan
  • 70,664
  • 5
  • 76
  • 161
  • 1
    I would unconditionally go with the list of weapons, even if all tanks have a cannon. No need for extra logic if you already have something generic in place. – Sebastian Redl Oct 21 '17 at 17:53
  • 1
    its tricky because of the example, but you can imagine if it was Move() all tanks might have treads and boss tanks might have extra smoke that needs to come out of the exhaust – Ewan Oct 21 '17 at 17:54