【问题标题】:Removing Switch Statements in Enums删除枚举中的 switch 语句
【发布时间】:2013-10-28 20:29:01
【问题描述】:

对于竞技场游戏,我有一个可能的游戏类型的枚举。这些游戏类型中的每一个都有一个与之关联的变体列表以及每个枚举的 2 个类,用于侦听事件和处理配置值。目前,我从枚举中构建这些侦听器和配置加载器。在这样做时,我必须使用 switch 语句。以下方法从枚举值中获取侦听器。

public GameListener getListener(CustomGame cg) {
    switch(this) {
    case Slayer:
        return new SlayerListener(cg);
    case Capture_The_Flag:
        return new CaptureTheFlagListener(cg);
    case Oddball:
        return new OddballListener(cg);
    case HeadHunter:
        return new HeadHunterListener(cg);
    case King_Of_The_Hill:
        return new KOTHListener(cg);
    default:
        return null;
    }
}

配置存在类似的代码。我知道使用抽象方法可以删除 switch 语句,但我想知道是否有更简单的方法。我试图让我的枚举采用 2 个类型参数,但我无法让它工作,而且我无法用它们构建。一种选择是传入Class<T> 并通过反射构造对象。

这个类中还有另一种方法,每个枚举都有非常不同的布局,我认为最好的选择是抽象方法,但我觉得这里有一个更简单的解决方案。

【问题讨论】:

  • 您的解决方案已经是最简单的解决方案(并且在性能方面也是最好的解决方案)。您可以通过将return 语句与case 语句放在同一行来使其看起来更简洁。
  • 如果性能在这里不计算在内(我想它不计算在内),并且您想优化案例以不需要在新枚举进入时对其进行维护,我建议您添加类以创建枚举的侦听器作为参数并在其上调用 newInstance()。在 Listener 接口上有一个 setCustomGame() 方法并调用它来初始化监听器。
  • @Robert 唯一的问题是每次添加枚举值时我都必须手动更新每个 switch 语句,这很常见。
  • @thst 这就是我想要发生的事情,只是我可以轻松地将自定义游戏作为参数传递给构造函数,而不是添加该方法。 (CustomGame 是最终版本,修改它会破坏很多。)
  • 这个设计对我来说看起来不错,虽然我宁愿创建一个ListenerFactory.createListenerFor(GameType type) 而不是直接使用枚举。枚举非常严格,因为您不能添加任何状态。就我个人而言,当GameType 创建一个听众时,我感觉很舒服,而且我现在非常愿意听这些迹象(只花了 25 年的时间开发:P)哦是的,如果可能的话,抛出一个IllegalStateException 而不是返回null

标签: java enums switch-statement


【解决方案1】:

这有点冗长,但你可以直接在枚举类型上提供工厂方法:

public enum GameType {
  SLAYER {
    @Override
    public GameListener getListener(CustomGame cg)
      return new SlayerListener(cg);
    }
  },
  // ... similarly for other GameListener types

  public abstract GameListener getListener(CustomGame cg);
}

这个解决方案的好处在于,如果不定义 getListener() 方法的实现,现在就不可能创建新的 GameType

【讨论】:

  • 如果每个 GameType 不会占用大量代码,我会使用这种方法。目前有 2 种方法需要这种实现,如果我让它们更加模块化,可能还会有更多。如果它不起作用,这绝对是我会考虑的替代方案。
  • 不幸的是,Java 经常出现最接近语义理想的实现也是一个更冗长的实现。在这种情况下,您通过枚举类型定义某种自定义行为,它可以是 this 或类似于您已经布置的 switch 解决方案。
  • 很好,我决定采用这个解决方案。它可能会占用更多空间,但现在可以在实例化中找到有关某个 GameType 的所有信息。
  • @CrypticStorm 如果您认为这是最佳答案,请不要忘记将其标记为已接受
【解决方案2】:

这是一种工厂方法,我认为在这种情况下使用开关是完全有效的。另一种方法是创建侦听器的继承层次结构,并声明对侦听器的(可能是抽象的)超类的引用,而在运行时您将为引用实例化正确的侦听器。也许太麻烦了,但更面向对象。这是需要大量类的良好 OO 设计与充满 switch 语句的代码之间的权衡,请自行选择。

【讨论】:

  • 我想我知道你的目标是什么,但是如果我添加一个枚举值,它需要更新,我试图避免这种情况。侦听器仅通过此类构建,我希望保留它。
【解决方案3】:

如果性能无关紧要,您可以使用这样的通用 GameListener Getter:

public Listener getListener( CustomGame cg ) {
    // implement some smart way to find your listener implementation here...
    Class<? extends Listener> clzz = Class.forName( this.getClass().getPackage() + "." + name() );
    Listener ret = clzz.newInstance();
    ret.setCustomGame(cg);
    return ret;
}

这将不需要维护,也不需要枚举中的特殊参数。但当然,它将枚举名称直接链接到创建的类名称,这可能不是所需的约束。

【讨论】:

    猜你喜欢
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多