safe - Avoding instanceof en Java
singleton class c# (7)
Como se mencionó en esta otra pregunta , los compiladores modernos de Java son muy eficientes en operaciones como instanceof . Deberías estar bien usándolo.
De hecho, una de las otras respuestas proporcionadas probó las comparaciones de instanceOf y string, y instanceOf fue significativamente más rápido. Te recomiendo que te quedes con su uso.
En mi aplicación tengo una 2da matriz de entidades para representar una grilla. Cada ubicación en la cuadrícula puede estar vacía u ocupada por una entidad (en este caso, es solo una persona o pared). En este momento utilizo instanceof
para verificar si una entidad es una persona o un muro.
Estaba pensando en darle a cada entidad un método que devuelva una enum
indique su tipo, es decir, una entidad de pared devolvería EntityType.WALL
. Me preguntaba si esta es la mejor idea para eliminar el uso de instanceof
o es una instanceof
adecuada en este escenario.
Dejaría que una persona y una pared heredasen de una superclase abstracta (por ejemplo, Tile) que tiene un método getType () que devuelve una enumeración o int e implementa este método en Wall y Person devolviendo el apropiado
La respuesta de Ligi está en el dinero. (Quien lo rechazó, me pregunto qué estaban pensando.) Como alternativa, considere esto:
abstract class Tile
{
public final EntityType type;
protected Tile( EntityType type )
{
this.type = type;
}
}
abstract class Pedestrian extends Tile
{
public Pedestrian()
{
super( EntityType.PEDESTRIAN );
}
}
abstract class Wall extends Tile
{
public Wall()
{
super( EntityType.WALL );
}
}
El fundamento de esto es que el "tipo" de la entidad es una característica permanente de la entidad, por lo que es adecuado especificarlo en el constructor y realizarse en un campo miembro final
. Si es devuelto por un método virtual (método no final en el lenguaje de Java), los descendientes serían libres de devolver un valor en un punto en el tiempo, y otro valor en otro punto en el tiempo, lo que deletrearía estragos.
Ah, y si realmente no puede soportar el miembro público final, siga adelante y agregue un getter para ello, pero mi consejo sería que no importa que los puristas, los miembros finales públicos sin getters estén perfectamente bien.
La solución teórica para eliminar la instanceof
de una manera refinada es el uso del patrón de visitante . La forma en que funciona es que el objeto que necesita saber si el otro elemento es un muro o una persona llama a ese objeto consigo mismo como parámetro, y ese objeto en particular vuelve a llamar para proporcionar información sobre su tipo.
Ejemplo,
public class Person {
void magic() {
if(grid.getAdjacent() instanceof Person) {
Person otherPerson = (Person)grid.getAdjacent();
doSomethingWith(otherPerson);
} else if(grid.getAdjacent() instanceof Wall) {
Wall wall = (Wall)grid.getAdjacent();
doOtherThingWith(wall);
}
}
}
Puede llegar a ser
public class Person extends Entity {
void magic() {
grid.getAdjacent().visit(this);
}
void onVisit(Wall wall) {
doOtherThingWith(wall);
}
void onVisit(Person person) {
doSomethingWith(person);
}
public void visit(Person person) {
person.onVisit(this);
}
}
public class Wall extends Entity {
public void visit(Person person) {
person.onVisit(this);
}
}
Las respuestas son muy buenas aquí, nada que decir al respecto, pero si estuviera en tal situación y si está permitido, me habría ido para una matriz int 2d con valor posible 0 (para vacío por asignación predeterminada) y 1,2 para persona o pared
Si sigue las otras respuestas aquí e implementa un patrón de visitante o usa una enumeración, no se equivocará.
Sin embargo, también puede ser útil pensar qué es exactamente lo que quiere hacer con esa lógica de conmutación (ya sea instancia o visitantes), porque a veces hay una forma más sencilla de hacerlo.
Por ejemplo, si todo lo que quiere hacer es verificar si una entidad ocupa una cuadrícula de una manera de bloqueo, entonces puede simplemente agregar un método boolean isSolid()
a cada entidad a través de la interfaz. Puedes usar esto con métodos predeterminados para una belleza extra:
public interface GridPhysics {
default boolean isSolid() {
return true;
}
// other grid physics stuff
}
public class Wall implements GridPhysics {
// nothing to do here, it uses the default
}
// in your game logic
public boolean canMoveTo(GridPhysics gridCell) {
return !gridCell.isSolid() && otherChecks();
}
También es posible que desee echar un vistazo a los sistemas componentes de las entidades (por ejemplo, Artemis ), que básicamente llevan esta idea de "composición sobre la herencia" al extremo.
Use Tell, Do not Ask : en lugar de preguntarle a los objetos qué son y luego reaccionar al respecto, dígale qué hacer y luego las paredes o las personas deciden cómo hacen lo que deben hacer.
Por ejemplo:
En lugar de tener algo como esto:
public class Wall {
// ...
}
public class Person {
// ...
}
// later
public class moveTo(Position pos) {
Object whatIsThere = pos.whatIsThere();
if (whatIsThere instanceof Wall) {
System.err.println("You cannot move into a wall");
}
else if (whatIsThere instanceof Person) {
System.err.println("You bump into " + person.getName());
}
// many more else branches...
}
haz algo como esto:
public interface DungeonFeature {
void moveInto();
}
public class Wall implements DungeonFeature {
@Override
public void moveInto() {
System.err.println("You bump into a wall");
}
// ...
}
public class Person implements DungeonFeature {
private String name;
@Override
public void moveInto() {
System.err.println("You bump into " + name);
}
// ...
}
// and later
public void moveTo(Position pos) {
DungeonFeature df = currentPosition();
df.moveTo(pos);
}
Esto tiene algunas ventajas.
En primer lugar, no necesita ajustar un árbol gigante si no más cada vez que agrega una nueva función de mazmorra.
En segundo lugar, el código en las características de la mazmorra es autónomo, la lógica está en todo en dicho objeto. Puedes probarlo y moverlo fácilmente.