What design pattern to use when you need derived class field values but you only have base class field variables?

Viewed 43

Consider the following code:

abstract class Player{
    abstract Stats getStats();
}

abstract class RangedPlayer:Player{
    RangedStats rangedStats;
    override Stats getStats() {return rangedStats;}
}

abstract class MeleePlayer:Player{
    MeleeStats meleeStats;
    override Stats getStats() {return meleeStats;}
}

class Stats{
    int maxHealth;
}

class RangedStats:Stats{
    int maxAmmo;
}

class MeleeStats:Stats{
    int meleeComboLength;
}

abstract class ThingPlayersCando{
    Player player;
    abstract void doThing(); 
}
class ThingMeleePlayersCando: ThingPlayersCando{
    abstract void doThing(){
        //getMeleeComboLength from base player class?
    }
}
class ThingRangedPlayersCando: ThingPlayersCando{
    abstract void doThing(){
        //maxAmmo from base player class?
    }
}

Here I made ThingMeleePlayersCanDo and ThingRangedPlayersCando both inherit from ThingPlayersCando because sometimes accessing the Player class is useful for both. But sometimes I may also want to access only things available to Melee or RangedStats.

The shared interface/abstract class is necessary because I want to call doThing() without needing to care about the type.

The getStats() in player class is necessary because somethings only need to know stuff shared by all players(e.g maxHP)

I thought of the following fixes:

  1. Just cast stats to appropriate type.
  2. Do away with Ranged and Melee Stats and put all fields in single Stats class. Simplest and it's not like having more data fields is going to hurt.
  3. Player stats is a dictionary/hashmap. Ugly.
  4. Pass in RangedStats/RangedPlayer in ThingRangedPlayersCanDo constructor. Same for melee.

Is there a design pattern that can solve this issue in a better way?

1 Answers

What design pattern to use when you need derived class field values but you only have base class field variables?

You can create instance of class that have desired values.

But sometimes I may also want to access only things available to Melee or RangedStats.

You can create an instance of Ranged stats:

RangedStats rangedStats = new RangedStats();
int health = rangedStats.maxHealth;

And IntelliSense will show you only its available methods and parameters:

enter image description here

I've edited your code because it does not compile:

abstract class Player
{
    public abstract Stats GetStats();
}

abstract class RangedPlayer : Player
{
    RangedStats rangedStats;

    public override Stats GetStats() { return rangedStats; }
}

abstract class MeleePlayer : Player
{
    MeleeStats meleeStats;

    public override Stats GetStats() { return meleeStats; }
}

class Stats
{
    int maxHealth;
}

class RangedStats : Stats
{
    int maxAmmo;
}

class MeleeStats : Stats
{
    int meleeComboLength;
}

and other classes:

abstract class ThingPlayersCando
{
    Player player;
    public abstract void DoThing();
}
class ThingMeleePlayersCando : ThingPlayersCando
{
    public override void DoThing()
    {
        throw new NotImplementedException();
    }
}
class ThingRangedPlayersCando : ThingPlayersCando

    public void OnlyMyMethod() { }


    public override void DoThing()
    {
        throw new NotImplementedException();
    }
}
Related