design-patterns - patterns - visitor patron java
¿Alternativa a instanceof? (4)
He oído que es un mal diseño usar instanceof o equivalente ( http://www.javapractices.com/topic/TopicAction.do?Id=31 , cuando deberíamos usar instanceof y cuándo no ) en el que puedo estar de acuerdo, principalmente porque puede dificultar la reutilización del código.
Sin embargo, en algunos casos me ha resultado difícil encontrar una buena alternativa a instanceof. Por ejemplo, di que quiero hacer un juego de estrategia en tiempo real. El juego consiste en obstáculos, edificios y tanques, todos ubicados en una grilla y cada entidad ocupa exactamente una unidad en la grilla. Entonces creo la clase Entity que es la superclase de las clases Obstacle, Building y Tank. La grilla consiste en instancias de Entidad. Durante cada actualización, quiero que cada tanque apunte y dispare sobre un tanque enemigo dentro del alcance. Entonces, una forma fácil de hacer esto sería que cada tanque solicite a la grilla todas las entidades dentro del rango de tanques y luego iterar sobre todas estas entidades y verificar si son una instancia de la clase Tank.
Mi único intento como alternativa de usar instanceof fue utilizar el patrón de diseño Visitor . El visitante es aceptado por una entidad (entity->acceptVisitor(visitor))
que a su vez llama a uno de los métodos visitor->visitObstacle(this)
, visitor->visitBuildig(this)
o visitor->visitTank(this)
. Esto, sin embargo, me obligó a crear muchos visitantes, casi uno nuevo por cada tarea que quería hacer en las entidades. Otro problema es que en muchos casos el visitante llama al mismo método en la entidad, sin importar de qué clase esté construida. Esto podría ocurrir, por ejemplo, cuando una entidad quiere verificar si otra entidad está estacionaria o no:
Código de 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
Podría, por supuesto, dejar que la entidad en este caso simplemente pregunte a otra entidad si es estacionaria directamente en lugar de dejar que un visitante lo haga. Pero en ese caso no sería coherente al verificar las propiedades de las entidades. Permitir que el método de preguntarle a las entidades sobre alguna propiedad (directamente oa través de un visitante) varíe, dependiendo de si necesito verificar el tipo de entidades o no, sería, en mi opinión, un diseño bastante extraño.
Entonces, ¿tiene alguna otra alternativa para usar instanceof en el problema descrito anteriormente?
¡Gracias! Martín
Bien, ¿ha considerado iterar sobre todos los tanques para ver si están dentro del alcance en lugar de todas las entidades dentro del alcance para ver si son tanques? Parece que le ahorraría mucho tiempo tanto en la iteración como en la invocación de llamadas ...
En general, el polimorfismo es la forma de evitar un operador de instancia innecesario.
No sé si necesita usar Visitor para manejar este comportamiento. Su caso podría lograrse fácilmente mediante el uso de polimorfismo general. Al principio estaba a punto de sugerir un método de fábrica y una variable de tipo, pero la solución podría ser incluso más simple.
Entonces tienes una Superclase abstracta general. (Entidad). Entonces en esta clase puedes definir un método llamado hitByMissile () (o lo que sea). En tu clase de tanques, puedes hacer hitByMissile, realizar de forma diferente, digamos un Obstáculo. Su código no debe decidir cómo se comportará cada entidad. El comportamiento debe ser definido por el objeto mismo. Entonces puede iterar sobre las entidades y llamar al método.
Olvidando su solución de Visitante por un segundo, y concentrándose solo en su requerimiento:
La grilla consiste en instancias de Entidad. Durante cada actualización, quiero que cada tanque apunte y dispare sobre un tanque enemigo dentro del alcance. Entonces, una forma fácil de hacer esto sería que cada tanque solicite a la grilla todas las entidades dentro del rango de tanques y luego iterar sobre todas estas entidades y verificar si son una instancia de la clase Tank.
¿Por qué no solo filtrar la lista directamente?
targetablesInRange = filter(isTargetable, grid.itemsInRangeOf(self))
En lugar de solo tanques, debería preguntar acerca de una propiedad de las entidades que los convierte en un objetivo. Esto podría devolver false en la clase base y ser anulado por Tank y otras clases que introduzcas más tarde que deberían activarse.