Подтвердить что ты не робот

Avoding instanceof в Java

В моем приложении у меня есть 2d-массив объектов для представления сетки. Каждое место в сетке может быть пустым или занято сущностью (в данном случае это просто человек или стена). Сейчас я использую instanceof, чтобы проверить, является ли объект лицом или стеной.

Я думал о предоставлении каждой сущности метода, который возвращает enum с указанием своего типа, так что объект стены вернет EntityType.WALL. Мне было интересно, если это лучшая идея, чтобы удалить использование instanceof или instanceof, подходящего в этом сценарии?

4b9b3361

Ответ 1

Используйте Скажите, не спрашивайте: вместо того, чтобы спрашивать объекты, какие они есть, а затем реагировать на это, расскажите объекту, что делать, а затем стены или люди решайте, как они делают то, что им нужно делать.

Например:

Вместо того, чтобы иметь что-то вроде этого:

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...
}

сделайте что-нибудь вроде этого:

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);
}

Это имеет некоторые преимущества.

Во-первых, вам не нужно настраивать гигантское дерево if then else каждый раз, когда вы добавляете новую функцию подземелья.

Во-вторых, код в функциях подземелья является самодостаточным, логика - все в указанном объекте. Вы можете легко протестировать его и переместить.

Ответ 2

Теоретическим решением для удаления instanceof в уточненным способом является использование шаблона посетителей. Принцип работы заключается в том, что объект, который должен знать, является ли другой элемент стеной или человеком, вызывает этот объект с самим собой как параметр, и этот конкретный объект обращается назад, тем самым предоставляя информацию о его типе.

Пример

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);
        }
    }
}

Может стать

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);
    }
}

Ответ 3

Если вы следуете другим ответам здесь и реализуете шаблон посетителя или используете перечисление, вы не ошибетесь.

Однако это может также помочь подумать о том, что именно вы хотите сделать с этой логикой коммутации (будь то экземпляр или посетитель), потому что иногда есть более простой способ сделать это.

Например, если все, что вы хотите сделать, это проверить, занимает ли объект сетка заблокированным способом, вы можете просто добавить метод boolean isSolid() к каждому объекту через интерфейс. Вы можете использовать это со стандартными методами для дополнительной красоты:

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();
}

Вы также можете взглянуть на системы компонентов сущностей (например, Artemis), которые в основном используют эту идею "наследование" до крайности.

Ответ 4

Я хотел бы, чтобы человек и стена наследовали от абстрактного суперкласса (например, Tile), который имеет метод getType(), возвращающий enum или int, и реализует этот метод в Wall and Person, возвращая соответствующий

Ответ 5

Ответы очень хороши здесь, ничего не говорится об этом, но если бы я был в такой ситуации, и если бы он был разрешен, я бы ушел бы за массив 2d int с возможным значением 0 (для пустого по умолчанию) и 1,2 для человека или стены.

Ответ 6

Как уже упоминалось в этом другом вопросе, современные компиляторы Java очень эффективны при таких операциях, как instanceof. Вы должны хорошо использовать его.

Фактически, один из других предоставил ответы на проверочные экземпляры instanceOf и сравнения строк, а instanceOf был значительно быстрее. Я рекомендую вам использовать его.

Ответ 7

Ответ лиги прямо на деньги. (Кто бы ни опронил его, интересно, о чем они думали.) В качестве альтернативы рассмотрим это:

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 );
    }
}

Обоснование этого заключается в том, что "тип" объекта является постоянной характеристикой объекта, поэтому он подходит для указания в конструкторе и реализуется в поле члена final. Если он возвращается виртуальным методом (не конечный метод в java-языке), тогда потомки будут иметь возможность возвращать одно значение в один момент времени и другое значение в другой момент времени, что может вызвать хаос.

О, и если вы действительно не можете выстоять перед публичным конечным участником, продолжайте и добавьте геттер для него, но мой совет будет заключаться в том, что неважно, пуристы, публичные конечные участники без геттеров - все в порядке.