Что является лучшей практикой программирования на Java: стекирование перечислений и конструкторов перечислений или создание подклассов?

Учитывая конечное число элементов, которые различаются по типу, лучше ли представлять их с помощью сложенных перечислений и конструкторов перечислений или создавать их подклассы? Или вообще есть лучший подход?

Чтобы дать вам некоторый контекст, в моей маленькой RPG-программе (которая по иронии судьбы должна быть простой), у персонажа есть различные виды предметов в его или её инвентаре. Предметы различаются в зависимости от их типа, использования и эффекта.

Например, один элемент инвентаря - это свиток заклинаний под названием Gremlin, который регулирует атрибут Utility. Другим предметом может быть меч по имени Морт, который используется в бою и наносит урон.

В моем коде RPG я попробовал два способа представления предметов инвентаря. Одним из способов было создание подклассов (например, InventoryItem -> Spell -> AdjustingAttributes; InventoryItem -> Weapon -> Sword) и создание экземпляров каждого подкласса, когда это необходимо, и присвоение таких значений, как имена, такие как Gremlin и Mort.

Другим способом было сложение перечислений и конструкторов перечислений. Например, я создал перечисления для itemCategory и itemSpellTypes и itemWeaponTypes, а перечисление InventoryItem было таким:

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. 

}

Есть ли лучшая практика программирования на Java, чем эти два подхода? Или, если это единственные способы, какой из двух лучше? Заранее спасибо за ваши предложения.

3 ответа

Решение

В контексте, который вы описываете, я хотел бы рассмотреть возможность использования иерархии классов в отличие от определений перечислений и дополнения этой иерархии интерфейсами; например

/**
 * 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 {
}

Основным преимуществом этого подхода является то, что он позволяет обрабатывать данный объект в разных контекстах (как InventoryItemс или в виде списка Usableс). Другая причина, по которой я бы держался подальше от перечислений, заключается в том, что вы, скорее всего, определите поведение своих предметов (например, spell.cast(Person)) и это не очень хорошо в перечислениях ИМХО.

Перечисления хороши, если в них содержатся только статические данные и очень минимальные логические (желательно вообще без логики). Если ваши объекты должны иметь различное поведение в зависимости от их вида, обычно рекомендуется реализовать их как подклассы.

Как уже упоминалось, вы, скорее всего, добавите атрибуты к своим объектам, и эти атрибуты не будут иметь смысла для всех видов. Например. атрибут цвета имеет смысл для оружия, но не для заклинания. В этом случае оружие и заклинание не должны быть в одном и том же перечислении.

Если вы хотите добавить новые атрибуты к элементам, может быть проще реализовать их как подклассы. Таким образом, если группа элементов получает новый атрибут, она распространяется на все подклассы.

Например, если все виды Weapon ему присваивается определенный вес, вы просто добавляете этот атрибут в этот класс. Этот атрибут будет затем распространяться на подклассы.

Что касается других вещей, которые являются более статичными по своей природе и, таким образом, могут быть непосредственно назначены типу, перечисления хороши. Я свободно думаю здесь, так что я думаю, Weapon может иметь DamageType назначено ему:

public abstract class Weapon {

public enum DamageType {

 CUT,
 SMASH,
 PIERCE;
}

private DamageType damageType;
Другие вопросы по тегам