instanceof的替代?

时间:2011-04-19 19:31:10

标签: design-patterns instanceof

我听说使用instanceof或等效的(http://www.javapractices.com/topic/TopicAction.do?Id=31when should we use instanceof and when not)是不好的设计,我可以同意,主要是因为它可以使代码难以重用。

然而,在某些情况下,我发现很难找到一个替代instanceof的好方法。例如,假设我想制作一个Real-Time-Strategy游戏。游戏由障碍物,建筑物和坦克组成,全部放置在网格上,每个权限仅占用网格中的一个单位。所以我创建了类Entity,它是类Obstacle,Building和Tank的超类。网格由实体的实例组成。在每次更新期间,我希望每个坦克瞄准并射击范围内的敌方坦克。因此,一个简单的方法是让每个坦克向网格询问坦克范围内的所有实体,然后迭代所有这些实体并检查它们是否是坦克类的实例。

我唯一尝试使用instanceof的方法是使用设计模式Visitor。访问者被实体(entity->acceptVisitor(visitor))接受,后者又调用其中一种方法visitor->visitObstacle(this)visitor->visitBuildig(this)visitor->visitTank(this)。 然而,这迫使我创建了大量访问者,对于我想在实体上完成的每一项任务,几乎都有一个新访问者。另一个问题是,在许多情况下,访问者在实体上调用相同的方法,无论它是从哪个类构造的。例如,当实体想要检查另一个实体是否静止时,就会发生这种情况:

Python代码:

class StationaryEntityGatherVisitor:
    def __init__(self):
        self.stationaryEntities = []

    def visitObstacle(self, obstacle):
        self._addIfStationary( obstacle )

    def visitBuildig(self, building):
        self._addIfStationary( building )

    def visitTank(self, tank):
        self._addIfStationary( tank )

    def _addIfStationary(self, entity):
        if entity.isStationary():
            self.stationaryEntities.append( entity )

    def getStationaryEntities():
        return self.stationaryEntities
当然,我可以让实体在这种情况下只询问另一个实体是否直接静止而不是让访问者这样做。但在这种情况下,我不会在检查实体的属性时保持一致。为了让实体询问某些属性(直接或通过访问者)的方法各不相同,这取决于我是否需要检查实体类型,在我看来,这似乎是一个非常奇怪的设计。

那么,您在上述问题中使用instanceof还有其他选择吗?

谢谢! 马丁

4 个答案:

答案 0 :(得分:1)

暂时忘记您的访客解决方案,并专注于您的要求:

  

网格由实体的实例组成。在每次更新期间,我希望每个坦克瞄准并射击范围内的敌方坦克。因此,一个简单的方法是让每个坦克向网格询问坦克范围内的所有实体,然后迭代所有这些实体并检查它们是否是坦克类的实例。

为什么不直接过滤列表?

targetablesInRange = filter(isTargetable, grid.itemsInRangeOf(self))

而不仅仅是坦克,你应该询问实体的属性,使它们成为目标。这可能会在基类中返回false,并被Tank和稍后引入的其他类重写,应该被触发。

答案 1 :(得分:0)

嗯,你考虑过迭代所有的坦克,看他们是否在射程而不是范围内的所有实体,看看他们是否是坦克?看起来它会在迭代和实例调用上节省大量时间......

答案 2 :(得分:0)

通常,多态性是避免不必要的instanceof运算符的方法。

答案 3 :(得分:0)

我不知道您是否需要使用Visitor来处理此行为。通过使用一般多态性可以很容易地完成您的案例。我最初打算建议一个工厂方法和类型变量,但解决方案可能更简单。

所以你有一个通用的抽象超类。 (实体)。所以在这个类中你可以定义一个名为hitByMissile()(或其他)的方法。在你的坦克类中,你可以制作hitByMissile,执行方式与让我们说障碍物不同。您的代码不应该决定每个实体的行为方式。行为应该由对象本身定义。所以你可以迭代实体并调用方法。