A question of OO structure...

Hello All,

I'll do my best to explain what I'm trying to accomplish and look forward to any and all suggestions you have for the "proper" way to do this (realizing there is no 100% correct way, but some methods are more optimal than others). I apologize in advance for my n00bness.

For this example, I'll use football (american) players.

I want my Player class to have a List<Stat> collection of my Stat class. Each Stat, which is an indicator of how well a player plays a particular position, should have a base value but be able to return a "true" value which is a calculation of the base value plus a percentage of one or more other stats. For example: a Player's Halfback stat might be base value plus 50% of the Fullback Stat base value and 10% of the Wide Receiver Stat base value.

Considering that every instance of the Player class should have the same number of Stat objects in it's collection, and each instance of a Halfback Stat should have the same modifiers, how would I set this up


Here is a sketch of my first stab at it:

First, a static class with a List<Stat> collection in it. This static class initiates 18 Stat objects into that collection with the proper names but zero values. I intend to use this static class to loop through when I create a new player, thus ensuring that the Quarterback Stat is always in ordinal position 0, Halfback Stat is always in ordinal position 1, Fullback at 2, etc...

Then I created a StatMod class which is just an integer for ordinal position of the modifying stat and a decimal for the percentage of that stat to use. My Stat class then has a List<StatMod> which will represent each modifying Stat for an instance of a Stat.

Back to the static class: in the constructor I initiate the 18 Stats that every player will have and then fill in the StatMod list in each of the 18 Stats. Basically, I'm using this static class as a sort of "Constant" for filling out new players when the time comes.

Does that sound okay to you guys There is more to go I realize, but I'm taking this one step at a time.

Thanks!




Answer this question

A question of OO structure...

  • Wolf5

    Guys,

    Thanks for the detailed replies!

    Fernando, your english was fine.

    Using inherited classes for each stat type had occurred to me. I'm not entirely certain why I wanted to avoid that, but it may have been due to the fact I didn't want to have to add an access property for each stat. Obviously, the generic GetStat method resolves that issue.

    The Enum type is new to me. That's what you get for no formal education I guess. F1 to the rescue! :)

    And having the Stat know it's owner makes a lot more sense that having the stat know the collection it belongs to which is how I was intendng to get the calculated value.

    And the way I'll definately avoid infinite loops in the calculating value is the calculated value will always reference the base value.

    I appreciate the ideas, thanks again!




  • CenGenTech

    Hey guys,

    Thanks again for all of your help.

    The following code DOES compile, with one warning:

    Field 'Player._stats' is never assigned to, and will always have it's default value null



    public enum Position
    {
    Quarterback,
    Halfback,
    Fullback,
    WideReceiver
    }

    public class Stat
    {
    protected float _baseValue;
    protected Player _owner;


    public Stat(Player owner)
    {
    _baseValue = 0F;
    _owner = owner;
    }

    public Player Owner()
    {
    return _owner;
    }

    public float GetBaseValue()
    {
    return _baseValue;
    }

    public virtual float GetCalculatedValue()
    {
    return 0F;
    }


    public virtual string Name()
    {
    return "";
    }

    public virtual string Desc()
    {
    return "";
    }

    public void SetBaseValue(float value)
    {
    _baseValue = value;
    }

    }

    public class QuarterBackStat : Stat
    {

    public QuarterBackStat(Player owner) : base(owner) {}


    public override string Name()
    {
    return "QB";
    }

    public override string Desc()
    {
    return "Quarterback";
    }

    public override float GetCalculatedValue()
    {
    return (_baseValue) + (_owner.GetStat(Position.WideReceiver).GetBaseValue() * .1F);
    }
    }

    public class HalfbackStat : Stat
    {

    public HalfbackStat(Player owner) : base(owner) { }

    public override string Name()
    {
    return "HB";
    }

    public override string Desc()
    {
    return "Halfback";
    }

    public override float GetCalculatedValue()
    {
    return (_baseValue) + (_owner.GetStat(Position.Fullback).GetBaseValue() * .5F);
    }
    }

    public class FullbackStat : Stat
    {

    public FullbackStat(Player owner) : base(owner) { }

    public override string Name()
    {
    return "FB";
    }

    public override string Desc()
    {
    return "Fullback";
    }

    public override float GetCalculatedValue()
    {
    return (_baseValue) + (_owner.GetStat(Position.Halfback).GetBaseValue() * .5F);
    }
    }

    public class WideReceiverStat : Stat
    {

    public WideReceiverStat(Player owner) : base(owner) { }

    public override string Name()
    {
    return "WR";
    }

    public override string Desc()
    {
    return "Wide Receiver";
    }

    public override float GetCalculatedValue()
    {
    return (_baseValue) + (_owner.GetStat(Position.Quarterback).GetBaseValue() * .1F);
    }
    }

    public class Player
    {

    private Hashtable _stats;

    public Player()
    {
    _stats.Add(Position.Quarterback, new QuarterBackStat(this));
    _stats.Add(Position.Halfback, new HalfbackStat(this));
    _stats.Add(Position.Fullback, new FullbackStat(this));
    _stats.Add(Position.WideReceiver, new WideReceiverStat(this));
    }

    public Stat GetStat(Position index)
    {
    return (Stat)_stats[index];
    }

    }



  • dr.acv

    Additional question I do have :)


    Doing it that way, a Player Stats are not indexed. This means my GetStat function in the Player class is just going to be a big Switch statement, correct Is that the best way to handle it It seems like an indexed collection would allow me to reference the stat with the ordinal position defined by the Enum.

    Thanks,




  • bw12117

    A few additional notes:

    I am *trying* for a certain amount of flexibility here. I'd like to be able to, with minimal work, add a Stat to that 18 in the future. If I decide further down the line I need a Kick Returner stat, I would like to just be able to add that to my static class and be done with it.


    I'm fairly new to OO programming, although I'm familiar with most of the concepts. I've been a VB and SQL programmer for years, which is why I seem to have a handle on some things but be clueless about others. I've never had any formal schooling or training, which is another reason my language may seem funny.

    Thanks again,



  • Kate Boothby

    I would use a different approach. My english is not that great... so I'll try to explain myself the best way I can. :D

    First of all, instead of a player having a list of stats, I would have a hash table of stats. This way I don't have to worry about indexing. To solve this, I'd simply create an enumeration for the different positions:



    public enum Position
    {
    Quarterback,
    Halfback,
    ...
    }

    Also, I would use the polimorphysm for different stats. This would involve that Stat would be the base class and QuarterbackStat would inherit from it. Stat would know the owner and QuarterbackStat would override the method CalculateValue. Finally in the constructor of Player I would create all individual stats.



    public class Stat
    {
    private float _baseValue;
    private Player _player;

    public Stat(Player owner)
    {
    _baseValue = 0F;
    _player = owner;
    }

    public Player OwnerPlayer
    {
    get {return _player;}
    }

    protected float BaseValue
    {
    get {return _baseValue;}
    }

    public void setBaseValue (float baseValue)
    {
    _baseValue = baseValue;
    }

    public virtual float CalculateValue ()
    {
    return _baseValue;
    }
    }

    public class QuarterbackStat
    {

    public override float CalculateValue()
    {
    return (_baseValue * 0.8F) + (OwnerPlayer.GetStat(Position.HalfBack).CalculateValue() * 0.2F);
    // Be careful here since this might go into an infinite loop.
    }

    }

    I'm kinda lazy to go through all of it, and in fact I coded it here, so I have no idea if it compiles... as long as you get the concept...

    If you want to go further into it, just keep asking...

    Regards,
    Fernando.


  • l Bllizzd l

    Hello All.

    Ben:

    If you're wanting a collection of stats for iteration, or to bind to a GUI control, define a GetStats() method that returns a collection. That way, you have control over the kind of collection it is, and the information contained in it.

    You can then use that collection in your calling code to do whatever you need to do.

    HTH.



  • wencey

    Hello All.

    Ben:

    Well, you don't need to make your Stat class static, and it doesn't have to be a collection. The "constant" concept you're thinking of will come from the structure of your classes.

    You can establish a base Stat class, that includes any information that is common to all of the types of stats. Then, for each position, establish a derived stat class that includes stat information that is unique to that position.

    Finally, you define a Player class, that along with other information about Players, includes a member for each of the derived position stat classes. Then, if you want to add a future position stat, you simply define a derived class for it, including any information that makes it unique, and add that to the Player class definition.

    Something like this:



    class Player


    {

    // ... other information concerning a player.


    private HalfBackStat halfback;

    private FullBackStat fullback;

    // ... and so on.


    }

    class Stat


    {

    private int baseAttribs;

    // ... any other info common to ALL stats.


    }

    class HalfBackStat : Stat


    {

    private int posAttribs;

    // ... any other info unique to this position.


    }

    class FullBackStat : Stat


    {

    // ... any other info unique to this position


    }


    You can have different initialization schemes by using overloaded constructors, and you don't need to use a loop.

    At first glance, I get the impression that you're trying to achieve flexibility in your calling code at the expense of structure in your classes. What you wind up with is your calling code knowing entirely too much about the internal workings of your classes. That violates encapsulation.

    Any information or utility that your calling code needs from your class should simply be available with a property or method call, like Player.HalfBackStat.info , or Player.FullBackStat.GetInfo() .

    HTH.



  • rebeccat

    Just include "_stats = new Hashtable()" at the begining of the player constructor.
  • Sumit Bhatnagar

    Of course... stupid me :)


    Thanks again, to both of you!

    Have a great holiday!




  • Ledeni_Plamen

    I didn't read your second post.

    Adding a new stat would be fairly simple. You would just need to add the new stat class, override the CalculateValue method, add this value to the enumeration, and add this stat to the player in the Player constructor.


  • Tamila

    Hi Ben... I'm glad I could help.

    The idea is not using the switch, that's why you would have a hash of stats. Look at the Hashtable class and to retrieve the stat you would simply have to do:



    private Hashtable _stats;
     
    ...
     
    _stats.Add (Position.Quarterback, new QuarterBackStat(this));
     
    ...
    ...
     
    public Stat GetStat(Position aPosition)
    {
       return (Stat)_stats[aPosition];
    }

     

    An alternative is using a simple collection, since the enumerations values are nothing but int, and you coud cast it to such.

    Note: This is within the Player class.


  • A question of OO structure...