【问题标题】:Choosing a subclass without a switch statement In Java在Java中选择没有switch语句的子类
【发布时间】:2016-10-11 20:19:42
【问题描述】:

我有一个名为 Enemy 的基类和各种 Enemy 类型的子类,例如 BigEnemy、LazerEnemy、AvoidingEnemy 等。

我有一个 Formation 类,其目的是创建专门的敌人阵型,例如线、网格、金字塔。

我希望 Formation 将我想要创建的 Enemy 子类的类型作为参数。

Formation f = new Formation("LazerEnemy","triangle", 4); // makes a triangle formation of lazer enmies
Formation f = new Formation("BigEnemy","line", 10); // makes a line of big enemies

目前我打算做一些事情,比如传递一个名为enemyType的字符串(或者它可能只是一个整数并执行switch语句),但由于我有这么多敌人类型,我想知道是否有一种更简洁的方法来传递我要实例化的不需要使用 switch 语句的对象的类型。

也许这与Factory和this问题有关,但我不太明白。

谢谢

【问题讨论】:

  • 为什么不能直接传递new LazerEnemy()new BigEnemy(),而Formation 构造函数将Enemy 作为第一个参数?
  • 您有三个选项,1) 使用条件分支开关或 if-else,2) 使用反射并传入类,例如LazerEnemy.class, 3) 有不同的工厂方法,例如Formation.createBigEnemy("line", 10);
  • @Tunaki 所以你是说,将子类对象作为参数传递给 Formation,如 Formation (new LazerEnemy(), "pyramid", 4),然后 Formation 的构造函数会做什么?我必须创建多个新的敌人类型。编队(敌人 e,字符串 ftype,整数) { } 。我现在需要创建多个敌人。我应该克隆 e 吗?
  • 哈,在这种情况下,如果你在 Java 8 下,我会传递一个Supplier<Enemy>。每次调用get() 都会返回一个新的敌人实例。然后你可以使用new Formation(BigEnemy::new, ...)

标签: java inheritance subclass


【解决方案1】:

您可以将类型作为参数传递给方法。

Formation f = new Formation(LazerEnemy.class, Shape.LINE, 4)

public formation(Class<? extends Enemy> enemyType, Shape shape, int num) {
    Enemy enemy1 = enemyType.newInstance(); 
    ...
}

不过你需要try

【讨论】:

  • 我在你发帖时正在输入这个!谢谢 :)。他还需要循环和一些收集敌人的方法。他可以在 Enemy.class 中创建一个静态类工厂,返回一个或多个敌人。此外,如果他想将参数提供给敌人,他可能不得不使用 newInstance 的 Constructor 方法。
【解决方案2】:

如果我理解正确,Formation 构造函数必须能够创建给定类型敌人的多个实例。不要将敌人的类型作为字符串或类传递,您应该简单地为 Formation 提供一种创建 Enemies 的方式,即 factory敌人的供应商

public clas Formation<E extends Enemy> {

    // we'll store them in a list
    private List<T> enemies;

    public Formation(Supplier<E> enemySupplier, int count) {
        enemies = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            enemies.add(enemySupplier.get());
        }
    }
}

如果 LazerEnemy 有一个无参数的构造函数,您可以通过这种方式创建 LazerEnemy 的 Formation:

Formation<LazerEnemy> f = new Formation<>(LazerEnemy::new, 10);

假设 LazerEnemy 构造函数需要一个强度来支持它的激光,你会使用

int strenth = 5;
Formation<LazerEnemy> f = new Formation<>(() -> new LazerEnemy(strength), 10);

所以,简而言之,让调用者决定并指定必须如何创建编队中的敌人,而不是强迫编队知道如何创建各种敌人。

【讨论】:

  • 是的,这是一个很好的方法:它解耦了逻辑。 Formation 有责任制造敌人的阵型。它没有责任制造敌人;它的目标是将它们排列成适当的形式。
  • 几个问题。在顶部,您有 Formation 。当我使用 arraylists n 这样时,我使用泛型,但是这里发生了什么?什么是E?第二.. 供应商是我必须做的一类吗?它是什么样子的? get 方法是否只返回一个新的 Enemy 对象?第三,双冒号运算符我以前见过,但不知道是什么意思。我现在没有使用 Java 8。以前也没见过箭头。
  • E是阵型中敌人的类型。如果你不需要它,你可以让你的类非泛型。但是如果你需要让敌人脱离你的阵型并知道他们的实际类型,那么你的类型应该是通用的。这是有道理的:如果我理解正确,编队是敌人的集合,就像列表一样。 E是阵型中包含的敌人类型。 Supplier是JDK的一个接口。你可以再做一个,但 Supplier 很合适。
  • 如果您没有使用 Java 8,那么请执行此操作。它已经推出很长时间了,没有理由不使用它。你也可以在 Java 7 中做到这一点,但是你必须使用 Supplier 之外的另一个接口,并且你必须使用类(通常是匿名内部类)而不是 lambda 表达式或方法引用。或者你可以使用 Kotlin,它与 Java 6 及更高版本兼容,并且也支持 lambda 表达式。
【解决方案3】:

我可能会使用工厂方法。

Class A

Class B extends A

Class C extends A

public static A factory(string)
  if(string.equal("b")) return new B
  if(string.equal("c")) return new C

这个伪代码的边缘可能有点粗糙,但它应该能说明问题。

【讨论】:

    【解决方案4】:

    有很多方法可以解决这个问题。其中之一是使用Dependency Inversion principle。 IE。 new Formation() 构造函数的调用者也可以创建敌人:

    Formation f1 = new Formation(new LazerEnemy(), "triangle", 4);
    Formation f2 = new Formation(new BigEnemy(), "line", 10);
    

    如果这些敌人有很长的构造函数,那么你可以使用依赖注入框架。 Google guice 相对易于设置。

    【讨论】:

      【解决方案5】:

      您应该通过创建一个 EnemyFactory 接口来解决,然后为您的所有 Enemy 类型创建 EnemyFactories 的特定实例。然后像这样使用它

      Formation f = new Formation(LazerEnemyFactory,FormationShape, 4);
      

      【讨论】:

      • 这应该是评论而不是答案。
      • @bhspencer 为什么?它回答了问题,而不是按照被问到的方式回答问题。
      • 我同意并努力使您的答案更具描述性。我知道你想告诉他什么,但是有很多 Java 工厂为什么不展示一个接近相关的例子,比如......implementing-factory-design-pattern-in-java
      猜你喜欢
      • 2014-04-14
      • 1970-01-01
      • 1970-01-01
      • 2012-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-16
      相关资源
      最近更新 更多