Search code examples
inheritancedesign-patternspolymorphismsubclasssuperclass

How to implement polymorphism if the class function definition depends on the subclass AND the subclass's relationship with another class type


This is a code design question. I am making a tile based game where one class character can traverse over a certain kind of tile while another class cannot. In this situation, there is warrior class that can walk over stone tiles. There is another class, called a “burrower” (digs underground), that cannot traverse over the stone tiles (i.e. cannot burrow into solid rock). My question is, how can I design the classes so that an entity of class type Warrior (inherits from Entity class) can traverse over a stone tile, while an entity of class type Burrower (also inherits from Entity class) cannot traverse over a stone tile?

The Entity class has the pure virtual function canTraverse(Tile). It takes a tile object as a parameter and checks if this entity can traverse it. My first thought went to polymorphism (http://www.cplusplus.com/doc/tutorial/polymorphism/) and how two different classes inheriting from the same super class can behave differently depending on the class type. My issue here, though, is how can I make the class Burrower behave differently from Warrior when the function canTraverse(Tile) not only depends on the subclass (Burrower or Warrior), but also its relationship to the type of tile?

I have an enum: Enum TileType {GRASS, STONE} The Tile class has a member variable tileType which is of type enum TileType. Subclasses of Tile include Stone and Grass, i.e. Stone and Grass classes both inherit from Tile. So, in the canTraverse function for the Burrower class, I would have something like this:

Boolean Warrior::canTraverse(Tile tile)
{
    // a warrior can traverse any kind of tile
    Return True;
}

Boolean Burrower::canTraverse(Tile tile)
{
    If(tile.getType() == STONE)
    {
        Return False;
    }
    Return True;
}

This should work, but it looks dangerously close to if I were calling instanceof:

If ( tile instanceof STONE)
    Return False;

Which is going against polymorphism (http://www.artima.com/interfacedesign/PreferPoly.html). Is there a better way/better coding practice I can follow to have the Burrower class figure out that it cannot traverse a tile that is of type STONE?


Solution

  • From what I understand, you want to be able to say whether character can traverse a tile. This functionality I'd extract to a separate class (say MapTraversal), which would take two parameters, a character and a tile and decide whether a traversal is possible.

    This class can rely on specifics, e.g. tile type, character type or other attributes like character strength or time of day.

    In this way you decouple both character and tile classes.

    Regarding your question, in your case you have to rely on specifics (attributes) which are part of the class, and if you had a stone subclass from tile you could introduce an interface istraversablefor(character), but still you'd have to check for the character type there. Hence my example above, to decouple this logic to a class which may actually need this information.

    You can also go a different route. You want to call a method based on two different kinds of objects. In this case you may want to utilize double dispatch which can be implemented by the visitor pattern. Double dispatch will make the programming language you are writing in, such as C++, choose the correct function to execute based on the types of the objects in question.