Given a finite number of items which differ in kind, is it better to represent them with stacked enums and enum constructors, or to subclass them? Or is there a better approach altogether?
To give you some context, in my small RPG program (which ironically is supposed to be simple), a character has different kinds of items in his or her inventory. Items differ based on their type and use and effect.
For example, one item of inventory is a spell scroll called Gremlin that adjusts the Utility attribute. Another item might be a sword called Mort that is used in combat and inflicts damage.
In my RPG code, I now have tried two ways of representing inventory items. One way was subclassing (for example, InventoryItem -> Spell -> AdjustingAttributes; InventoryItem -> Weapon -> Sword) and instantiating each subclass when needed, and assigning values such as names like Gremlin and Mort.
The other way was by stacking enums and enum constructors. For example, I created enums for itemCategory and itemSpellTypes and itemWeaponTypes, and the InventoryItem enum was like this:
public enum InventoryItem {
GREMLIN(itemType.SPELL, itemSpellTypes.ATTRIBUTE, Attribute.UTILITY),
MORT(itemType.WEAPON, itemWeaponTypes.SWORD, 30);
InventoryItem(itemType typeOfItem, itemSpellTypes spellType, Attribute attAdjusted) {
// snip, enum logic here
}
InventoryItem(itemType typeOfItem, itemWeaponTypes weaponType, int dmg) {
// snip, enum logic here
}
// and so on, for all the permutations of items.
}
Is there a better Java programming practice than these two approaches? Or if these are the only ways, which of the two is better? Thanks in advance for your suggestions.
In the context you describe I would consider using a class hierarchy as opposed to enum definitions and supplementing this hierarchy with interfaces; e.g.
/**
* Root of class hierarchy.
*/
public interface InventoryItem {
}
/**
* Additional "parallel" interface implemented by some (but not all)
* InventoryItems and other non-inventory items.
*/
public interface Usable {
void use();
}
/**
* A Spell is in InventoryItem and is also Usable.
*/
public abstract class Spell implements InventoryItem, Usable {
}
public class Gremlin extends Spell {
}
/**
* A Door is *not* an InventoryItem but can be used.
*/
public class Door implements Usable {
}
The main advantage of this approach is that it allows you to treat a given object in different contexts (as InventoryItem
s or as a list of Usable
s). The other reason I would steer clear away from enums is that you're likely to define behaviour on your items (e.g. spell.cast(Person)
) and this doesn't sit well in enums IMHO.