【问题标题】:Am I using instanceof "wrong"?我使用的是“错误”的 instanceof 吗?
【发布时间】:2014-06-20 04:53:10
【问题描述】:

所以在我的一个简单游戏引擎中,我使用了一个接口“UpdatedGameElement”来表示必须通过一个 update() 方法的实现,每帧更新具有该接口的对象。

现在在我的“主”(不是主)类中,我遍历 GameElement 的列表并检查这些是否是更新游戏元素的实例。如果是这种情况,我会强制转换它们,然后调用 .update()。

现在问题是,我刚刚读到使用 instanceof 通常是编码错误的标志;如果类被用作标记,而它们可以很容易地被变量替换,我同意。但我不太确定自己的情况。

我想我可以让 GameElement 类实现 UpdatedGameElement,并定义一个标准的空 update() 方法,该方法需要被覆盖才能真正做某事,但我不确定这是否以及为什么会比我拥有的更好现在。

你会说什么?

编辑:我的主类中的一些代码:

public void process()
{
    if (active)
    {
        for (GameElement GE: elements)
        {
            if (!GE.isToBeRemoved())
            {
                //Relevant part
                if (GE instanceof UpdatedGameElement)
                {
                    ((UpdatedGameElement) GE).update();
                }           
            }
            else
            {
                prepareRemoval(GE);
            }
        }  
        processRemovals();
    }
}

【问题讨论】:

  • 为什么不更新所有的 GameElements?还是将它们存储在自己的列表中?
  • 我们需要看代码。 instanceof 不一定是坏事,但这种情况表明你最好持有一个单独的数据结构,它只包含可更新的对象。
  • 您确定有必要将所有 GameElement 保存在同一个列表中吗?你在哪里创建/添加元素到列表elements
  • 为什么不是每个GameElement 都有一个update()?如果在某些情况下它是无操作的,那么它比instanceof 测试更糟糕。我认为这是一个见仁见智的问题。关键问题是这个,它有效吗?
  • @DavidEhrmann:GameElement 类不可更新。到目前为止,一个类必须将 UpdatedGameElement 实现为“可更新”(如具有 void update() )。是的,你是对的:我可以让 GameElement 自己实现它,但我很好奇你是否可以同时使用这两种方法。 (至于为什么:我目前的课程会使类更干净,因为它们不需要子类可能使用一千种方法。)还发布了一些代码:)

标签: java oop instanceof


【解决方案1】:

如果 OP 发出邀请:

如果接口的使用除了在 GE 中添加一个标记和更新方法之外没有其他原因,并且如果除了这个单个 instanceof 之后不使用 UGE 类型,那么拥有这些额外类型的理由很弱. E特别是当被更新的能力可以扩展到所有其他 GE 时,它只是一个 NOOP。

基类中的抽象方法迫使程序员决定是否需要为特定子类编写更新代码。从“防御性设计”的角度来看,这种方法相当“安全”。但是,当然,您要编写更多代码。

与之前的技术相反:如果您忘记了界面,则没有警报。

此外,如果您在基类中编写 NOOP 更新方法并依靠程序员的敏捷性在必要时进行覆盖:方便,但如果您忘记这样做,则会有风险。

总结:有一些微妙的优点和缺点 - 不仅仅是“气味”的实例。

【讨论】:

  • 完美的答案涵盖了我有疑问的每一点:) 谢谢。
【解决方案2】:

根据您提到的 cmets,GameElement 实现了 UpdatedGameElement,目前 GameElement 是唯一实现 UpdatedGameElement 的类,但将来可能会更多。另外我希望您知道无法实例化接口,因此您无法创建 UpdatedGameElement 的实例,因此实际上实例是由接口的实现类创建的。因此,在运行时,当您创建 GameElement 的实例并将其分配给 UpdatedGameElement 变量时,这并不意味着该实例现在是 UpdatedGameElement 类型,它实际上是 GameElement 类型,现在假设您还有一个实现 XYZElement 的类实现了 UpdatedGameElement 并创建实例如下:

UpdatedGameElement ge = new GameElement();
UpdatedGameElement xyze = new XYZElement();

您认为使用下面的检查实例是否很好,因为无论哪种情况都是正确的,您永远不知道 ge 和 xyze 属于哪种实例。

if(ge instance of UpdatedGameElement)
instead one should always check for if(ge instance of GameElement)

Similarly for if(xyze instance of UpdatedGameElement)
instead one should always check for if(ge instance of XYZElement)

Hope this helps.

【讨论】:

  • UpdatedGameElement 是一个接口。所以实现它的每个对象实际上都是 UpdatedGameElement 类型。
  • GameElement 也是抽象的,应该是所有其他 GameElement 的基类(就像 java 的 Object 类一样)。所以不应该真的有冲突,因为你不能实例化它,并且没有其他类具有引擎绝对必要但在 GameElement 中没有的东西。我要问的是,如果我可以在我概述的情况下使用我的 UpdatedGameElement 界面作为标记。如果我没有很好地概述,我很抱歉。
【解决方案3】:

我想我可以让 GameElement 类实现 UpdatedGameElement,并定义一个标准的空 update() 方法 需要被覆盖才能真正做某事

是的,你绝对应该这样做!

原因:假设将来您需要实现另一个扩展 UpdatedGameElement 的“可更新”类 - 考虑在您使用过的每个地方都必须进行的代码更改instanceof...

【讨论】:

  • 我不必做出改变吧? UpdatedGameElement 的唯一用途是表示在扩展对象内部存在一个可调用的 update() 方法。该方法的作用是在每个单独的类中定义的。
  • @JonasBartkowski 你肯定做出改变,除非新类扩展/实现UpdatedGameElement。现在,如果新类不能扩展/实现UpdatedGameElement(并且我至少可以想到几个你无法做到的场景) - 你将不得不添加另一个instanceof 签入每个放入你使用的代码中。
  • 不。如果它不实现 UpdatedGameElement,它应该什么都不做,所以不存在如果不实现就必须改变的特殊行为。
  • @JonasBartkowski 你没看懂我写的 - 请再读一遍
  • 如果是这种情况,那么您可以使用它。这不是我所说的“干净”代码,但您不是第一个使用标记模式接口(可序列化、可克隆等)的人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-14
  • 1970-01-01
  • 2015-09-02
  • 2018-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多