1

我正在设计一款游戏,我对自己正在做的事情有很好的了解。

但是我一直在努力提高我的 OOP 技能,但时不时会遇到同样的问题,我应该如何使用抽象对象?

假设我有一个实体列表,它代表屏幕上具有 x 和 y 属性的任何东西,并且宽度和高度可能还没有完全弄清楚!

然后我有特殊类型的实体,一种可以移动,一种不能移动,将来可能会发生碰撞。

它们都在实体的集合中(List<Entity>在我的情况下),现在我想模拟移动的实体并且是主循环上 DynamicEntity 的实例,但它们都在实体的抽象列表中,我不知道是实体在循环中动态实体与否。

我知道我可以检查一下,instanceof但我很确定这不是最好的主意..

我见过一些人在实体中使用类似布尔值来检查它的类型,但我真的不想在那里硬编码所有类型的实体。

我只想知道在这种情况下最好的做法是什么?

4

3 回答 3

5

通常,如果可能,最好避免检查类型。如果您认为需要instanceof在代码中使用,那么您可能可以使用抽象来使您的设计更具可扩展性。(如果您决定Entity在将来添加第三种类型,您不希望返回并instanceof使用第三种情况更新所有支票。)

有两种常用方法可以根据实例的具体类型执行不同的操作,而无需检查具体类型:

一种常见的方式是访问者模式。这里的想法是为每种类型的对象创建一个带有操作的访问者类。接下来,每个具体类都有一个accept方法,该方法简单地调用visit访问者类中的正确方法。这种单级间接允许对象自己选择正确的操作,而不是您通过检查类型来选择它。

访问者模式通常用于以下两个原因之一。1) 您可以向实现访问者模式的类层次结构添加新操作,而无需访问类的源代码。您只需实现一个新的访问者类并将其与可访问类的预先存在的accept方法一起使用。2)当有许多可能的操作可以对某个类型层次结构中的类执行时,有时将每个操作拆分到它自己的访问者类中会更清楚,而不是用一堆不同操作的一堆方法污染目标类,所以您将它们与访问者而不是目标类分组。

然而,在大多数情况下,第二种方式更容易做事:简单地覆盖每个具体类中公共方法的定义。该类Entity可能有一个抽象draw()方法,然后每种类型的实体将以draw()不同的方式实现该方法。您知道每种类型的 Entity 都有一个draw()可以调用的方法,但您不必知道它是哪种类型的实体或方法的实现做了什么的详细信息。您所要做的就是遍历您的List<Entity>并调用draw()每一个,然后它们将根据其类型自行执行正确的操作,因为每种类型都有自己的专门draw()实现。

于 2012-08-20T15:01:17.327 回答
2

您是对的,您不想检查实例类型或具有某种功能来检查功能。我的第一个问题是 - 为什么您首先拥有该基本类型的实体列表?在我看来,您需要维护一个动态实体列表。

可以实现一个move()对非动态实体不执行任何操作的方法,但在此特定场景中这似乎并不正确。

也许最好实现一个触发该列表迭代的事件,并将该事件依次传递给每个对象。动态实体可以决定继续该事件。静态实体显然不会。

例如

   Event ev = ...
   foreach(e : entities) {
      e.actUpon(ev);
   }

在这种情况下,您可以有不同的事件类型,实体将根据事件类型实体类型来决定它们的动作。这称为双重调度访问者模式

于 2012-08-20T14:59:36.050 回答
1

如果您对实体的处理依赖于了解有关实体类型的详细信息,那么您的Entity抽象不会给您带来太多好处(至少在这个用例中不会):您List<Entity>对您来说几乎和List<Object>.

如果您知道您可以想象的每个实体都是静态的或动态的,那么对所有实体都具有布尔属性就没有“硬编码”:isDynamic()或其他东西。

但是,如果动态方面仅对您的实体子集有意义,则此标志确实会给您的抽象带来一些混乱。在这种情况下,我的第一个猜测是您没有正确建模用例,因为您需要处理一个项目列表,这些项目没有提供足够的多态信息来处理它们。

于 2012-08-20T15:02:11.447 回答