javaenumssubclassingmultiple-constructors

Which is better Java programming practice: stacking enums and enum constructors, or subclassing?


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.


Solution

  • 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 InventoryItems or as a list of Usables). 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.