如何避免instanceof调用?

时间:2010-08-11 18:29:28

标签: java refactoring

我定义了这个简单的方法:

public static boolean isBorder(int x, int y) throws CollisionDetectionException {
        try {
            if ( (levelItems[x][y] instanceof StaticGameObject && levelItems[x][y].isVisible()) ||
                (levelItems[x-1][y] instanceof StaticGameObject && levelItems[x-1][y].isVisible()) ||
                (levelItems[x][y+1] instanceof StaticGameObject && levelItems[x][y+1].isVisible()) ||
                (levelItems[x][y-1] instanceof StaticGameObject && levelItems[x][y-1].isVisible()) ||
                (levelItems[x-1][y-1] instanceof StaticGameObject && levelItems[x-1][y-1].isVisible()) ||
                (levelItems[x-1][y+1] instanceof StaticGameObject &&levelItems[x-1][y+1].isVisible()) ||
                (levelItems[x+1][y] instanceof StaticGameObject && levelItems[x+1][y].isVisible()) ||
                (levelItems[x+1][y+1] instanceof StaticGameObject && levelItems[x+1][y+1].isVisible()) ||
                (levelItems[x+1][y-1] instanceof StaticGameObject && levelItems[x+1][y-1].isVisible()) ) {
                return true;
            } else {
                return false;
            } 
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new CollisionDetectionException("Collision couldn't be checked because checking position " + x + "/" + y + " caluclated values below (0/0)");
        }
    }

如你所见,我有一个2维数组。现在我想检查一个特定的位置((x / y) - >方法参数),如果在2维数组的相邻字段中有任何可见的StaticGameObject。

levelItems数组由所谓的GameObject组成。 StaticGameObject是GameObject的直接子类。

任何提示如何改进此方法?

7 个答案:

答案 0 :(得分:13)

向GameObject添加方法

bool isBorderObject() { return false; }

然后在StaticGameObject中重写

bool isBorderObject() { return true; }

将测试更改为

(levelItems[x][y].isBorderObject() && levelItems[x][y].isVisible())

此外,if可以嵌套为

for (int i = x-1; i <= x+1; ++i) {
   for (int j = y-1; j <= y+1; ++j) {
       GameObject item = levelItems[i][j];
       if (item.isBorderObject() && item.isVisible()) 
           return true;
   }
}
return false;

答案 1 :(得分:3)

(免责声明:我参与了在很多移动设备上运行的Java游戏)

人们已经展示了如何简化所有这些 if 语句。我想补充一点,定义你自己的CollisionDetectionException可能完全有点过分了。

首先,在OO级别不存在这样的低级Java细节:异常,尤其是已检查的异常,是真正不必要的Java特性。一些非常好且非常令人印象深刻的Java框架主要与它们一样,就像Spring一样。然后,许多非常令人印象深刻和功能强大的应用程序,由数以百万计的代码行组成,运行得非常好,没有使用检查异常的概念,因为它们很好地用语言编写,没有这种类型的概念(再次:在OO级别不存在“已检查的异常”,因此任何OOA / OOD到OOP而不需要使用已检查的异常)。

在任何情况下:确实没有理由将ArrayIndexOutOfBoundException转换为您自己的特定已检查异常。这意味着您计划使用异常进行流量控制,这是一个 HUGE no-no。

然后,关于你的“isBorder”测试...你可能不需要那样。你认为你这样做,但你真的没有。你为什么想知道它是否是“边界”?为了检测碰撞,我想。

你正在使用静态方法来检测它是否是边界,静态是OO的反论点。重要的是您在对象之间传递的消息。您可能有所有对象响应某些 isCollidingWith(...)消息:“border”对象知道它们是边框,他们将知道如何处理 isCollidingWith(。 ..)消息。

然后你可以更进一步:你可以有一些 resolveCollisionWith(...)方法而不是 isCollidingWith(...)

public class Wall implements MySuperAbstraction {

    int wallHitPoints = 42;

    boolean isWallStillUp = true;

    void resolveCollisionWith( SomeObject o ) {
        if ( o.isStrongEnoughToHarmWall ) {
           wallHitPoints--;
           isWallStillUp = wallHitPoints > 0;
        }
    }

}

这只是一段快速的代码,并没有处理 SomeObject 在他撞到墙壁时需要反弹的事实等等,但重点是:在OO对象中知道他们之间如何沟通。他们知道如何处理传递的各种信息。

如果你想做OO,那么我可以告诉你的是,Java的静态 instanceof 和已检查的异常绝对不是那样的。基本上,他们是OO的反论文:) ]

答案 2 :(得分:2)

这应该摆脱你拥有的极大if块:

for(int col = x-1; col <= x+1; col++)
{
    for(int row = y-1; row <= y+1; row++)
    {
        if(levelItems[row][col] instanceof StaticGameObject && levelItems[row][col].isVisible())
            return true;
    }
}

(这个解决方案只会减少疯狂if,而不是摆脱instanceof,正如你所看到的那样)

当然,在我的示例和你的示例中,都应该确保检查数组边界问题。

答案 3 :(得分:2)

对@ Lou的解决方案的修订,即有一个方法和一个循环。

for (int i = 0; i < 9; i++) 
   if (levelItems[x-1 + i/3][y-1 + i%3].isBorderObjectVisible())  
       return true; 
return false; 

答案 4 :(得分:1)

想想“控制倒置”。

作为示例,如何将此方法引入GameObject类:

public boolean
isBorder()
{
    return false;
}

并在StaticGameObject类中重写:

public boolean
isBorder()
{
    return self.isVisible();
}

简化上面的代码?

答案 5 :(得分:1)

另一个不错的技术是拥有一个返回相邻单元格集的函数。通过这种方式,您可以避免(或无论如何移动)双循环。更好地分离关注点;当你发现你的算法忘记了越界条件时,只有一个地方可以修复它。

  public Set<GameObject> adjacentItems(int x, int y) {
    Set<GameObject> set = new HashSet<GameObject>();
    for (int i = -1; i < 2; ++i)
      for (int j = -1; j < 2; ++j)
        set.add(levelItems[x+i][y+j]);
    return set;
  }

  public boolean isBorder(int x, int y) {
    for (GameObject item : adjacentItems(x, y))
      if (item.isBorder() && item.isVisible())
        return true;
    return false;
  }

答案 6 :(得分:0)

不要让它变得更复杂。只需确保GameObject的所有子类都实现isVisible。这就是全部。

如果您需要同时区分可移动与不可移动,那么您需要两种方法:

  • isVisible - 可见或不可见

  • isMovable - 可移动或不移动

您永远不需要instanceof

[问题尚不清楚,但似乎StaticGameObject类实际上意味着GameObject的子类isMovable()为false。]