Я изучаю Java (относительно новый), работаю над клоном игры на Java в качестве побочного проекта. В игре игрок управляет персонажами. Каждый персонаж является частью фракции, и у каждой фракции есть определенный список навыков. Навыки одной фракции не могут быть использованы другой фракцией. Моя идея организовать это с помощью вложенных перечислений, где основное перечисление Skills
имеет несколько внутренних перечислений (Faction1
, Faction2
и т. д.). Идея состоит в том, что я смогу получить доступ к данным для любого конкретного навыка, используя что-то вроде Skills.Faction1.SKILL_NAME
, и иметь доступ к полному списку навыков фракции, используя Skills.Faction1.values()
. Упрощенный пример неудачной реализации этого выглядит следующим образом:
public enum Skills {
FACTIONLESS_SKILL("arbitraryArgs"); //More skills than just this one
enum Faction1 {
FACTION1_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION1_LAST_SKILL("arbitraryArgs");
...
}
enum Faction2 {
FACTION2_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION2_LAST_SKILL("arbitraryArgs");
...
}
String arbitraryField; //1 of many fields universal among both factionless and factioned skills
Skills(String arbitraryArgs) { //1 of many universal constructors
this.arbitraryField = arbitraryArgs;
}
void doUniversalThing() {
//Code here
}
}
Когда я пытаюсь использовать эту реализацию, я получаю сообщения об ошибках, сообщающих мне, что конструкторы и/или поля для значений во внутренних перечислениях не существуют. Я попытался скопировать конструкторы и поля в каждое отдельное внутреннее перечисление, однако это было нечитаемо и не соответствовало потенциальным будущим навыкам, которые не будут привязаны к фракции. Этот упрощенный пример даже не включает все методы и альтернативные конструкторы, которые должны быть доступны для каждого навыка. Как я мог реализовать эту идею эффективно и элегантно таким образом, чтобы одновременно поддерживались навыки, которые являются членами фракции, и навыки, которые не являются, при условии, что я вообще мог бы реализовать ее? Я изо всех сил старался объяснить предполагаемые результаты кода, но если что-то все еще неясно, просто дайте мне знать. Спасибо.
Редактировать: было запрошено содержимое Faction1
, поэтому в дополнение к тому, что я переписал свой первоначальный пример кода, чтобы, возможно, дать лучшее представление о моих намерениях, вот несколько разных способов, которые я пробовал Faction1
. Все они были либо ошибочными, либо просто не идеальными.
Попытка 1:
public enum Skills {
FACTIONLESS_SKILL("arbitraryArgs"); //More skills than just this one
enum Faction1 {
FACTION1_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION1_LAST_SKILL("arbitraryArgs");
}
String arbitraryField; //1 of many fields universal among both factionless and factioned skills
Skills(String arbitraryArgs) { //1 of many universal constructors
this.arbitraryField = arbitraryArgs;
}
}
Моя первая попытка была именно такой, которая выдала мне ошибку The constructor Skills.Faction2(String) is undefined
. Я понимаю, что это связано с тем, что Faction2
является собственным классом и не может использовать конструктор Skills
, поэтому я перешел ко второй попытке.
Попытка 2:
public enum Skills {
FACTIONLESS_SKILL("arbitraryArgs"); //More skills than just this one
enum Faction1 {
FACTION1_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION1_LAST_SKILL("arbitraryArgs");
String arbitraryField; Duplicates of Skills fields
Faction1(String arbitraryArgs) { //Duplicate of Skills constructor
this.arbitraryField = arbitraryArgs;
}
}
String arbitraryField; //1 of many fields universal among both factionless and factioned skills
Skills(String arbitraryArgs) { //1 of many universal constructors
this.arbitraryField = arbitraryArgs;
}
}
Это решение технически работает, ошибок нет. Однако моя проблема с этим решением заключается в безумном количестве дублирования кода, которое это вызывает в моей несокращенной программе. Каждый навык имеет множество полей, конструкторов и методов, независимо от того, назначен навык фракции или нет. Есть также множество фракций, которые необходимо создать. Если бы я когда-нибудь понял, что поле, метод или конструктор либо не нужны и должны быть удалены, либо необходимы и должны быть созданы, мне нужно будет создать или удалить их из каждой фракции отдельно. Честно говоря, это не то, чем я хочу заниматься в глупом побочном проекте.
Я не думал о каком-либо другом способе создания этих внутренних перечислений и не видел их в своих исследованиях, поэтому пока это мои единственные две реализации. Надеюсь, это немного прояснит ситуацию.
Я благодарю вас за то, что вы занимаетесь своим обучением с помощью побочных проектов.
Рассматривали ли вы возможность сделать faction
атрибутом Skills
, а затем просто сделать SNIPER_SHOT
и JUDGEMENT
значениями Skills
? В зависимости от того, как вы пишете свой код, вы должны иметь возможность перечислять навыки по фракциям, если это было причиной, по которой вы сделали их отдельными перечислениями.
@RobSpoor Я обновил свой вопрос, указав содержание Faction1
, а также в целом улучшил пример кода, чтобы попытаться немного прояснить ситуацию.
Подумайте о том, чтобы сделать дробь enum
и использовать ее так же, как любой другой параметр.
Краткий урок ООП для начала -
Сущности Faction
и Skill
, похоже, имеют отношения has a
, то есть A Factions HAS A set of Skills
. Также я создам дополнительное перечисление SkillName
, которое снова имеет отношение HAS A
к Skill
. Итак, помня об этом, вы можете организовать свои перечисления так:
public enum Faction {
FACTION1(new HashMap<SkillName, Skill>(){{
put(Skill.SNIPER_SHOT.skillName(), Skill.SNIPER_SHOT);
}}),
FACTION2(new HashMap<SkillName, Skill>(){{
put(Skill.JUDGEMENT.skillName(), Skill.JUDGEMENT);
}});
Map<SkillName, Skill> skills;
Faction(Map<SkillName, Skill> skills) {
this.skills = skills;
}
public Skill[] skills(){
return this.skills.values().toArray(new Skill[0]);
}
public Skill skill(SkillName name){
Skill skill = this.skills.get(name);
if (Objects.isNull(skill)){
throw new IllegalArgumentException("Invalid Skill name");
}
return skill;
}
}
enum SkillName {
SNIPER_SHOT("SNIPER_SHOT"),
JUDGEMENT("JUDGEMENT");
String value;
SkillName(String value){
this.value = value;
}
}
enum Skill {
SNIPER_SHOT(SkillName.SNIPER_SHOT, 0, 95, 5, new boolean[] {true, true, false, false}, new boolean[] {false, true, true, true}),
JUDGEMENT(SkillName.JUDGEMENT,-25, 85, 5, new boolean[] {true, true, false, false}, new boolean[] {true, true, true, true});
SkillName name;
Integer dmg;
Integer acc;
Integer crit;
boolean[] rank;
boolean[] target;
Skill(SkillName name, Integer dmg, Integer acc, Integer crit, boolean[] rank, boolean[] target) {
this.name = name;
this.dmg = dmg;
this.acc = acc;
this.crit = crit;
this.rank = rank;
this.target = target;
}
public SkillName skillName() {
return this.name;
}
}
Теперь вы можете получить доступ к навыкам и конкретному навыку, например, --
Faction.FACTION1.skills();
Faction.FACTION2.skills();
Faction.FACTION1.skill(SkillName.SNIPER_SHOT);
Faction.FACTION1.skill(SkillName.JUDGEMENT); // this should throw exception
Faction.FACTION2.skill(SkillName.SNIPER_SHOT); // this should throw exception
Faction.FACTION2.skill(SkillName.JUDGEMENT);
Шаблоны доступа не совсем такие, как вы хотели, но они работают примерно в той же степени.
ПРИМЕЧАНИЕ -
Не знаю, какой тип игры вы разрабатываете, но в большинстве игр навыки игроков улучшаются (т. е. изменяются свойства навыков), а также игроки приобретают новые навыки. Следовательно, приведенная выше настройка станет очень жесткой, поскольку перечисления неизменяемы, и вы не сможете масштабировать это. Вместо этого я бы предложил создать классы для фракций и навыков вместо перечислений. Таким образом, вы сможете их мутировать.
Спасибо за ваше предложение, однако я не упомянул в посте, что я пришел к этой идее именно по той причине, что у избегать есть карты. Моя система до этой новой идеи синтаксиса была чем-то вроде Map<String, Skills[]> factionSkills = new HashMap<String, Skills>() {{put("FACTION_NAME", new Skills[] {SKILL1...});}}
. Однако, если я не смогу придумать лучшее решение, я обязательно запомню ваше! Благодарю вас!
Просто решил перейти на Java 9, чтобы использовать Map.of()
. Спасибо за вашу помощь!
Ваш ответ может быть улучшен с помощью дополнительной вспомогательной информации. Пожалуйста, редактировать добавьте дополнительную информацию, например цитаты или документацию, чтобы другие могли подтвердить правильность вашего ответа. Дополнительную информацию о том, как писать хорошие ответы, можно найти в справочном центре.
Похоже, вы уже нашли свое решение, но вот что-то, что, вероятно, намного проще в использовании.
import java.util.Arrays;
import java.util.List;
/** Enums. https://stackoverflow.com/questions/71517372 */
public class SOQ_20220406_0201
{
/**
*
* Main method.
*
* @param args commandline arguments, should they be needed.
*
*/
public static void main(String[] args)
{
enum Faction
{
F1,
F2,
F3,
ALL,
;
}
enum Skill
{
SKILL_1(Faction.F1, "arbitraryArgs"),
SKILL_2(Faction.F2, "arbitraryArgs"),
SKILL_3(Faction.F3, "arbitraryArgs"),
SKILL_ALL_CAN_USE(Faction.ALL, "arbitraryArgs"),
;
private final Faction faction;
private final String arbitraryArgs;
Skill(Faction faction, String arbitraryArgs)
{
this.faction = faction;
this.arbitraryArgs = arbitraryArgs;
}
public static List<Skill> fetchAllSkillsForFaction(final Faction faction)
{
return
Arrays.stream(values())
.parallel()
.filter(each -> each.faction == faction)
.toList()
;
}
}
System.out.println(Skill.fetchAllSkillsForFaction(Faction.F1));
}
}
Можете ли вы показать полное содержимое перечисления
Faction1
? Потому что вы упустили вещи, которые определенно имеют отношение к делу. Сами константы можно опустить (оставим первую и последнюю), но все остальное здесь очень важно.