-3

So let's say you have a Creature class. Now say you have a Hostile class that extends it, and a Passive class that extends it, for hostile and passive creatures respectively. If some creatures can fly and some can't it would make sense also for there to be a "Flying" class that extends Creature and also maybe a "Nonflying" class or something. But any class you make extending either Hostile or Passive couldn't also extend Flying or Nonflying. What's the solution for this? I want certain behaviors to only happen for some Creatures marked as flying, and some only to occur if the creature is non-flying, but I don't want to use a boolean as that would require checking every tick whether the creature is flying or not, and seems like an illegant solution. Any ideas?

So far I haven't known what to try, as implementing this through booleans seems clunky and inefficent.

  • @IłyaBursov 's comment is a good one. The answer is, it depends on the requirements of the program. But let me tell you, in the real world, this is big big problem that comes up a LOT. You start off with one set of requirements, and then half way through (or when you're doing version 2) the requirements change, and commonly create massive ETL headaches (https://learn.microsoft.com/en-us/azure/architecture/data-guide/relational-data/etl) - The point is to try to get your requirements as nailed down as possible before you start coding, and then to create agile code – ControlAltDel Dec 16 '22 at 21:26

1 Answers1

0

A common mistake that newcomers to object-oriented programming make is to try to fit every property of complex objects into one inheritance hierarchy. Inheritance is the most tightly coupled form of making a class X depend on a class Y. If X extends Y, then every change to Y must necessarily affect X, and all of X's methods must be careful not to violate Y's invariants (see Liskov substitution principle, for instance).

Instead of shoving everything into one hierarchy, think about how your objects may have properties that can be modelled more neatly. You're right to be hesitant to use a bunch of Booleans, but that's not what I'm suggesting. The flying/non-flying problem is orthogonal to the hostile/passive problem, so they need different solutions.

For the former, you might consider giving every creature type a Movement field. Perhaps something like

interface MovementAbility {
  ...
}

class FlyingAbility implements MovementAbility {
  ...
}

class WalkingAbility implements MovementAbility {
  ...
}

Then every creature gets one of these

class Creature {
  private MovementAbility movementAbility;
  ...
}

or multiple, if there are creatures who can walk and fly and who need to be able to decide which one to do.

class Creature {
  private List<MovementAbility> movementAbilities;
  ...
}

Then you can do the same thing for hostility. Something like

interface Disposition {
  ...
}

class HostileDisposition extends Disposition {
  ...
}

class PassiveDisposition extends Disposition {
  ...
}

Then your Creature could look like

public class Creature {
  private MovementAbility movementAbility;
  private Disposition disposition;
}

and the creature can call methods on the Disposition object to determine how to behave in the presence of a player, then on the MovementAbility object to determine how to get from A to B.

Exactly what methods go in each of those classes is dependent on your needs for your particular project, but the key thing to aim for is not doing instanceof checks. It's really easy to write code in Creature that sits there and says if (movementAbility instanceof FlyingAbility) { ... }, but that's no better than just using a Boolean in the first place. Instead, when you need to do something that depends on how a creature moves, make it an abstract part of MovementAbility and call that function virtually.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116