【问题标题】:Why shouldn't these interfaces inherit from each other?为什么这些接口不应该相互继承?
【发布时间】:2017-10-05 04:14:56
【问题描述】:

我发布了一个question,关于我在 Effective Java 中读到的接口和建议。我得到了我想要的答案,一切都很好,直到今天下午我登录 SO,并留下评论说

这些接口不应相互继承。

我问为什么,但仍然没有收到答案。有人可以解释为什么您不允许这些接口继承吗?您将如何修复它,并且仍然保留答案中提供的相同功能?

public interface GameObject {
    void viewDetails();
}

public interface Weapon extends GameObject {
    void attack();
}

//A weapon is not the only item reloadable. A container could be refilled. 
public interface Reloadable extends GameObject {
    void replenish (final int amount);
}

我看到这些接口不继承的唯一原因是GameObject 接口的简单性。它没有提供toString 无法完成的任何功能。然而,这只是我游戏的开始阶段,GameObject 当然会扩展。

【问题讨论】:

  • 继承意味着“is-a”关系。因此,武器是游戏对象。如果根据您的模型这是正确的,那很好。但是如果一个武器有一个游戏对象,那么它应该有一个GameObject getGameObject() 方法。如果两者都不是,则它不应该extend 也没有getGameObject() 方法。
  • @AndyTurner - 但在我的游戏中,它“是一个”游戏对象。从逻辑上讲,对我来说,物品和武器就是游戏对象。我知道如果它不是 GameOject,它不应该扩展,但这里不是这种情况。
  • “如果根据你的模型是正确的,那很好”。这只是一个人的评论,他们不应该扩展,没有人同意支持它。
  • @AndyTurner - 你会为此获得赞成票!

标签: java oop inheritance interface


【解决方案1】:

我认为您过于担心设计的纯度,以至于您关心琐碎的细节而不是高效地花费时间,即编写游戏。

无论哪种方式都没有太大区别。

通常,“GameObject”(以及名称以“Object”结尾的任何东西)是一个类,如果需要是抽象的,但不是接口。将接口命名为“SomethingObject”的唯一原因是该接口是否还扩展(或通过聚合公开)Closeable (AutoCloseable) 接口,这意味着任何引用它的人都可能破坏它。所以,如果你走这条路,剩下的问题就很容易回答了:接口 Weapon 可能不会扩展 GameObject,因为接口可能不会扩展类,Reloadable 也是如此,我们就完成了。

现在,如果您坚持使用“GameObject”接口,您仍然可以选择为每个附加特性扩展它,或者将每个附加特性分开,而不扩展“GameObject”。两种方法都可以。

如果你承诺,如果你以你所珍视的一切的名义发誓,永远不会有一个不是 GameObject 的 Reloadable,那么你可以继续让 Reloadable 扩展 GameObject。但你真的需要吗?通常的做法是,我们更喜欢使用接口来增加继承树的分支程度,而不是将继承树变成图。 (并不是说它永远不会发生,但它很少发生。)所以,我们通常不这样做。但如果你这样做了,那还不是世界末日。

还请注意,当我们从另一个接口扩展一个接口时,我们(并非总是如此)通常会扩展名称。因此,您的 Weapon 将是 WeaponGameObject,而您的 Reloadable 将是 ReloadableGameObject。

还要注意,接口继承很少(如果有的话)是必要的;你可以让 Reloadable 成为一个独立的接口(不扩展 GameObject),你可以让 Reloadable 包含一个GameObject getGameObject() 方法,从而基本上将两个实体链接在一起。然后,您的 BFG9000ReloadableGameObject 类可以同时实现 Reloadable 和 GameObject 并将 getGameObject() 实现为 return this;

【讨论】:

  • 感谢您的回答。我使用了 GameObject 接口,因为如果它是一个抽象类,继承会很深(Gun extends Weapon extends GameObject 等),并且有很多文章解释了为什么这是不好的。我不介意 getter,但我最近遇到了一些建议,说接口中的 getter 是代码异味(正如你所指出的,我太担心它了。)根据我的游戏逻辑,这是有道理的。跨度>
  • 另一位评论者回答了,但我不太确定我理解他的意思。
【解决方案2】:

做一个真正的领域分析。你会发现具有replenish 行为的东西,Replenishable,不一定是Weapon,并且在补充它是GameItem 时可能不在乎。 Vehicle 也可能需要补货。也许像

public interface Weapon extends GameItem {
public class ChargeGun 
  implements Replenishable, Weapon {...

public class FlyingCarpet extends GameItem
  implements Replenishable, Vehicle {...

通常让一个类做接口混合比接口继承它们更好。这取决于域以及如何对其建模。尽可能多的可行

gun.replenish();

不是

replenish(Replenishable Replenishable);

YMMV.

【讨论】:

  • 这就是 Reloadable 没有扩展武器的原因(并非所有武器都是可重装的,例如一把剑)。 Reloadable 可以应用于容器(盒子、杯子)。你的例子似乎有点不对劲,第一个例子中的 GameItem 是一个接口,第二个例子中,它是一个类,除非你的意思是实现。现在,补充需要数量。如果玩家要升级并获得更大的弹药筒,那么他们现在可以重新加载 10 发子弹/弹药,而不是 5 发/子弹,如果不是参数,如何补充知道数量?
  • 只调用replep本身不带参数是不够的。如果他们的库存中有墨盒,他们就可以将其作为参数传递给补给方法。
  • 大概Collection<Cartridge> 的某种形式将成为Player 实例状态的一部分,是吗?因此,如果Player 实例引用是由Player 通过new ChargeGun(this) 注入的,那么您只需要一个简单的chargeGun.replenish(); 或类似的东西。它将使用自己的final Player player 成员的player.getCartridges() 而不是方法参数。我并不是说这是正确的模式甚至是成语。只有一种方法。有很多路径。
【解决方案3】:

在这里同意迈克的观点:可能不是你应该担心的阶段。

对于游戏开发建模,通常会避免使用面向对象原则的“世界”。这是由于对象和性能的强耦合的原因。非常先进的游戏引擎不会为世界上的实体创建特定的类。

如果您对更详细的信息感兴趣, 看一下这个: http://entity-systems-wiki.t-machine.org/

【讨论】:

    猜你喜欢
    • 2012-10-23
    • 2017-04-14
    • 1970-01-01
    • 2014-06-01
    • 1970-01-01
    • 2016-04-11
    • 2015-10-18
    • 1970-01-01
    • 2014-09-09
    相关资源
    最近更新 更多