HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Structuring Class Objects And Defining The Variables

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
theobjectsstructuringvariablesandclassdefining

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) {
t

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 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 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 /* or Set? */ getAbilities();
  Attack getAttack();
  Attribute getPrimaryAttribute();
  AttributeValue getAttributeValue(Attribute attr);
  Map 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.

Code Snippets

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

interface Hero {
  // ...
  AttributeValue getStrength();
  AttributeValue getAgility();
  AttributeValue getIntelligence();
  // ...
}
enum Attribute {
  STRENGTH, AGILITY, INTELLIGENCE;
}

interface Hero {
  // ...
  Attribute getPrimaryAttribute();
  AttributeValue getAttributeValue(Attribute attr);
  Map<Attribute, AttributeValue> getAttributes();
  // ...
}
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();
  // ...
}
/* 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();
}
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

Context

StackExchange Code Review Q#58162, answer score: 2

Revisions (0)

No revisions yet.