带有构造函数和方法的嵌套枚举?

Nested Enums with Constructors & Methods?

我是一名 Java 学生(相对较新),在 Java 中开发游戏克隆作为副项目。在游戏中,玩家控制角色。每个角色都是派系的一部分,每个派系都有一套技能列表。一个派系的技能不能被任何其他派系使用。我关于如何组织它的想法是使用嵌套枚举,其中一个主要 Skills 枚举有多个内部枚举(Faction1Faction2 等)。我的想法是,我将能够使用类似 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
    }
}

当我尝试使用此实现时,我收到错误消息,告诉我内部枚举中值的构造函数 and/or 字段不存在。我尝试将构造函数和字段复制粘贴到每个单独的内部枚举中,但是这既不可读又不适应未来可能的技能,这些技能不会附加到一个派系。这个简化的示例甚至不包括每个技能必须访问的所有方法和备用构造函数。假设我完全可以实现这个想法,我如何才能以既支持派系成员技能又不支持派系成员技能的方式有效而优雅地实现这个想法?我尽力解释代码的预期结果,但如果仍有任何不清楚的地方,请告诉我。谢谢。

编辑: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 是它自己的 class 并且无法使用 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;
    }
}

此解决方案技术上 有效,因为没有错误。但是,我对这个解决方案的问题是在我的非简化程序中导致了大量的代码重复。每个技能都有许多字段、构造函数和方法,无论该技能是否分配给一个派系。还需要建立许多派系。如果我意识到某个字段、方法或构造函数是不需要的,应该删除,或者是需要的,应该创建,我将需要分别从每个派系中创建或删除它。老实说,这不是我想在愚蠢的副项目上做的事情。

我没有想过任何其他方法来创建这些内部枚举,也没有在我的研究中看到任何其他方法,所以这些是我迄今为止仅有的 2 个实现。我希望这能解决一些问题。

简短的 OOP class 首先是 -- FactionSkill 实体似乎具有 has a 关系,即 A Factions HAS A set of Skills。此外,我将创建一个额外的枚举 SkillName,它与 Skill 再次具有 HAS A 关系。所以记住这些你可以像这样安排你的枚举--

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);

访问模式与您想要的不完全相同,但它们的工作方式几乎相同。

边注-

不确定您正在开发什么类型的游戏,但在大多数游戏中,玩家的技能会提高(即技能属性会发生变化)并且玩家也会获得新技能。因此上面的设置将变得非常严格,因为枚举是不可变的,你将无法扩展它。相反,我建议您为派系和技能创建 classes 而不是枚举。这样你就可以改变它们。

刚刚决定升级到 Java9 以使用 Map.of()。谢谢大家的帮助!

看来您已经找到了解决方案,但这里有一些可能更容易使用。

import java.util.Arrays;
import java.util.List;

/** Enums.  */
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));
   
   }

}