Structuring Class Objects And Defining The Variables

Posted on

Problem

I am trying to create an outline of what a character is in a game.

Then I want to create 10 different characters and compare their stats at different levels.

I am creating an interface called Hero that looks like this,

public interface Hero {

    // define methods that must be defined across all character types
    public int getLevel();
    public String getPrimaryAttribute();
    public String getAttackType();
    public String getAbility1();
    public String getAbility2();
    public String getAbility3();
    public String getAbility4();
    public double getStrength();
    public double getStrengthMultiplier();
    public double getAgility();
    public double getAgilityMultiplier();
    public double getIntelligence();
    public double getIntelligenceMultiplier();
    public int getHealth();
    public int getMana();
    public int getDamageMin();
    public int getDamageMax();
    public int getRange();
    public double getArmor();
    public int getMovement();

}

Then I am creating each character like this,

 public class DrowRanger implements Hero {  

    private int level = 0;
    private String primaryAttribute = "Agility";
    private String attackType = "Ranged";
    private String ability1 = "Frost Arrows";
    private String ability2 = "Gust";
    private String ability3 = "Precision Aura";
    private String ability4 = "Marksmanship";
    private double strength = 17;
    private double strengthMultiplier = 1.9;
    private double agility;
    private double agilityMultiplier = 1.9;
    private double intelligence = 15;
    private double intelligenceMultiplier = 1.4;
    private int health = 473;
    private int mana = 195;
    private int damageMin = 44;
    private int damageMax = 55;
    private int range = 625;
    private double armor = 0.64;
    private int movement = 300;

    //default constructor
    public DrowRanger() {

    }

    // override as many times as you'd like
    public DrowRanger(int level) {
        this.level = level;
    }

    // getters and setters - required to implement ALL from interface
    public int getLevel() {
        return this.level;
    }

    public String getPrimaryAttribute() {
        return this.primaryAttribute;
    }

    public String getAttackType() {
        return this.attackType;
    }

    public String getAbility1() {
        return this.ability1;
    }

    public String getAbility2() {
        return this.ability2;
    }

    public String getAbility3() {
        return this.ability3;
    }

    public String getAbility4() {
        return this.ability4;
    }

    public double getStrength() {
        return this.strength;
    }

    public double getStrengthMultiplier() {
        return this.strengthMultiplier;
    }

    public double getAgility() {
        return this.agility;
    }

    public double getAgilityMultiplier() {
        return this.agilityMultiplier;
    }

    public double getIntelligence() {
        return this.intelligence;
    }

    public double getIntelligenceMultiplier() {
        return this.intelligenceMultiplier;
    }

    public int getHealth() {
        return this.health;
    }

    public int getMana() {
        return this.mana;
    }

    public int getDamageMin() {
        return this.damageMin;
    }

    public int getDamageMax() {
        return this.damageMax;
    }

    public int getRange() {
        return this.range;
    }

    public double getArmor() {
        return this.armor;
    }

    public int getMovement() {
        return this.movement;
    }

// This is where the setters are.

    public void setLevel(int level) {
        this.level = level;
    }

    public void setPrimaryAttribute(String primaryAttribute) {
        this.primaryAttribute = primaryAttribute;
    }

    public void setAttackType(String attackType) {
        this.attackType = attackType;
    }

    public void setAbility1(String ability1) {
        this.ability1 = ability1;
    }

    public void setAbility2(String ability2) {
        this.ability2 = ability2;
    }

    public void setAbility3String(String ability3) {
        this.ability3 = ability3;
    }

    public void setAbility4(String ability4) {
        this.ability4 = ability4;
    }

    public void setStrength(double strength) {
        this.strength = strength;
    }

    public void setStrengthMultiplier(double strengthMultiplier) {
        this.strengthMultiplier = strengthMultiplier;
    }

    public void setAgility(double agility) {
        this.agility = agility;
    }

    public void setAgilityMultiplier(double agilityMultiplier) {
        this.agilityMultiplier = agilityMultiplier;
    }

    public void setIntelligence(double intelligence) {
        this.intelligence = intelligence;
    }

    public void setIntelligenceMultiplier(double intelligenceMultiplier) {
        this.intelligenceMultiplier = intelligenceMultiplier;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public void setMana(int mana) {
        this.mana = mana;
    }

    public void setDamageMin(int damageMin) {
        this.damageMin = damageMin;
    }

    public void setDamageMax(int damageMax) {
        this.damageMax = damageMax;
    }

    public void setRange(int range) {
        this.range = range;
    }

    public void setArmor(double armor) {
        this.armor = armor;
    }

    public void setMovement(int movement) {
        this.movement = movement;
    }

} // End DrowRanger Class

Am I on the right path here?

Solution

Modifiers

All methods declared in an interface are automatically public and abstract. Some people prefer putting one or both in anyway, but most seem to prefer that they simply be left out:

public int getLevel();int getLevel();

Group related concepts

The Hero interface is a good start, but it has a lot of methods to implement, and that’s going to become tiresome when you need to make changes (I’m really lazy in these things). So let’s see whether we can extract some lumps and crops.

For example, you have pairs of closely related getters: getX() and getXMultiplier(). We can regroup these in a class (or interface) of their own:

interface AttributeValue { // [1]
  double getBase();
  double getMultiplier();
}

interface Hero {
  // ...
  AttributeValue getStrength();
  AttributeValue getAgility();
  AttributeValue getIntelligence();
  // ...
}

1 (Note that I opt for interfaces here; I tend to go with interfaces when I’m designing/freewheeling, and later on decide which ones will become classes. AttributeValue seems to be duct tape holding related values together, so it’s a good candidate for being a class.)

Now, if we later decide to add functionality–say, we add a flat addition bonus–we won’t need to rewrite our Hero interface and add three or more methods.

But if we want to add another attribute? We’d still need to change the Hero interface. Hrmph.

This is where enums will come in handy:

enum Attribute {
  STRENGTH, AGILITY, INTELLIGENCE;
}

interface Hero {
  // ...
  Attribute getPrimaryAttribute();
  AttributeValue getAttributeValue(Attribute attr);
  Map<Attribute, AttributeValue> getAttributes();
  // ...
}

The damage values also seem to be groupable, so let’s huddle them together in a class or interface of their own; same with life and mana:

enum AttackType {
  // ...
}

interface Attack {
  int getMinimumDamage();
  int getMaximumDamage();
  int getRange();
  AttackType getType();
}

interface Vitals {
  int getHealth();
  int getMana();
  double getArmour(); // bit of a corner case
}

interface Hero {
  // ...
  Attack getAttack();
  Attribute getPrimaryAttribute();
  AttributeValue getAttributeValue(Attribute attr);
  Map<Attribute, AttributeValue> getAttributes();
  Vitals getVitals();
  // ...
}

That leaves us with abilities. I’m not sure how ‘complicated’ abilities will be in your design, so I’m going to use mostly the same approach as before:

/* Don't forget to override equals(Object) and hashcode() for this. */
interface Ability {
  String getName();
}

interface Hero {
  int getLevel();
  List<Ability> /* or Set<Ability>? */ getAbilities();
  Attack getAttack();
  Attribute getPrimaryAttribute();
  AttributeValue getAttributeValue(Attribute attr);
  Map<Attribute, AttributeValue> getAttributes();
  Vitals getVitals();
}

Getting the data in

Then I am creating each character like this,

You are a more patient person than I am.

There are two ways to deal with scenario’s like this, depending on the relation of your data to your application:

  • Your data is external in nature. Extract the data to somewhere else (database, flat file, XML file), and then parse and read it into your application. JAXB can help you cobble together an XML bridge quickly, or you can write a simple key=value file.

  • You will need to create programmatical, on-the-fly instances. Create a builder class that helps you piece together an instance, like so:

    HeroBuilder.newBuilder()
           .attribute(STRENGTH, 17, 1.9)
           .attribute(AGILITY, 17, 1.9)
           .attribute(INTELLIGENCE, 15, 1.4)
           .health(473)
           .mana(195)
           //...
           .build(); // creates a Hero instance
    

Which one is right for you will depend on your application.

Leave a Reply

Your email address will not be published. Required fields are marked *