【问题标题】:How can I avoid instanceof when function behavior depends on type?当函数行为取决于类型时,如何避免 instanceof?
【发布时间】:2020-03-25 11:51:49
【问题描述】:

我正在设计一个基本的游戏引擎。我有一个抽象的GameObject 类,它由PlayerBullet 两个类继承。我正在尝试处理两个游戏对象的碰撞。如果两个碰撞的对象相同,则不会发生任何事情。如果其中一个是子弹,另一个是玩家,则玩家的生命值设置为 0。这是一个显示所需方法和类定义的 sn-p:

public interface GameObject {
    void onCollision(GameObject gameObject);
}

public class GameObjectImpl implements GameObject {
    ...
    // By default, do nothing.
    @Override
    public void onCollision(GameObject object) {}    
}


public class Player extends GameObjectImpl {
   ...
   @Override
   public void onCollision(GameObject gameObject) {
      if (gameObject instanceof Player) {
          // Do nothing
      } else if (gameObject instanceof Bullet) {
          this.health = 0;
      }
   }
}

我想避开instanceof,但我想不出任何办法。我想使用polymorphism 来实现它,因为GameObject 接口的客户端不应该知道有关子弹或玩家等的详细信息。它只会为每一帧中的对象调用onCollision 方法。如何使用更简洁的代码实现这一目标?谢谢。

【问题讨论】:

  • 这种继承层次结构不太好。虽然玩家和子弹可能都是抽象意义上的“游戏对象”,但它们几乎没有共同之处。无法很好地编写方法来处理不同的游戏对象,因此您必须求助于instanceof。这就是为什么游戏引擎倾向于使用Entity-Component-System 模型,它避免了继承(以及composition should be preferred over inheritance 无论如何)。

标签: java polymorphism instanceof


【解决方案1】:

这可能看起来很麻烦,因为 Java 解析目标方法的机制不是最好的,但你可以尝试类似的东西

public class Player extends GameObjectImpl {
   ...
    @Override
    public void onCollision(GameObject gameObject) {
        gameObject.collideWith(this);
    }
}

然后使用适当的“回调”扩展GameObject

public interface GameObject {
    void onCollision(GameObject gameObject);
    void collideWith(Player player);
}

然后

public class Player extends GameObjectImpl {
   ...
    @Override
    public void onCollision(GameObject gameObject) {
        gameObject.collideWith(this);
    }

    @Override
    public void collideWith(Player player) {
        // ... do nothing, shouldn't happen
    }
}

public class Bullet extends GameObjectImpl {
   ...
    @Override
    public void onCollision(GameObject gameObject) {
        gameObject.collideWith(this);
    }

    @Override
    public void collideWith(Player player) {
        player.setHealth(0);
    }
}

为了进一步阅读,您可以搜索 Java 中的访问者模式。

【讨论】:

  • 其实这不是“访问者”模式,而是访问者模式使用的“双重调度”。将双重派遣误称为访客是一个常见的错误。不过,这是最好的方法。
  • 这就是我写“Java 中的访问者模式”的原因;-) 但是,“Java 中的访问者实现”可能不会那么令人困惑。
  • 但这根本不是访客模式。一点也不。
  • 没错,但我没有写我实现了访问者模式
  • 但是,我最终会得到许多重载的collideWith 方法并且它们会被客户端看到(我倾向于避免),这不是很糟糕吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 2021-04-02
  • 2021-03-01
相关资源
最近更新 更多