Альтернатива instanceof?

Я слышал, что использование instanceof или его эквивалента является плохим проектом (http://www.javapractices.com/topic/TopicAction.do?Id=31, Когда следует и не следует использовать instanceof?), с которым я могу согласиться, главным образом потому, что это может затруднить повторное использование кода.

Однако в некоторых случаях мне было трудно найти хорошую альтернативу instanceof. Например, скажем, я хочу сделать игру в жанре стратегии в реальном времени. Игра состоит из препятствий, зданий и танков, размещенных на сетке, и каждый объект занимает ровно одну единицу в сетке. Поэтому я создаю класс Entity, который является суперклассом классов Obstacle, Building и Tank. Сетка состоит из экземпляров Entity. Во время каждого обновления я хочу, чтобы каждый танк целился и стрелял по вражескому танку в пределах досягаемости. Таким образом, простой способ сделать это для каждого резервуара — запросить у сетки все объекты в пределах диапазона резервуаров, а затем выполнить итерацию по всем этим объектам и проверить, являются ли они экземплярами класса 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 в описанной выше проблеме?


person DysproS    schedule 19.04.2011    source источник


Ответы (4)


На секунду забудьте о своем решении для посетителей и сосредоточьтесь только на своем требовании:

Сетка состоит из экземпляров Entity. Во время каждого обновления я хочу, чтобы каждый танк целился и стрелял по вражескому танку в пределах досягаемости. Таким образом, простой способ сделать это для каждого резервуара — запросить у сетки все объекты в пределах диапазона резервуаров, а затем выполнить итерацию по всем этим объектам и проверить, являются ли они экземплярами класса Tank.

Почему бы просто не отфильтровать список напрямую?

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

Вместо того, чтобы просто танки, вы должны спрашивать о свойстве сущностей, которое делает их целью. Это может вернуть false в базовом классе и быть переопределено Tank и другими классами, которые вы введете позже, по которым следует запустить.

person Lou Franco    schedule 19.04.2011

Что ж, не рассматривали ли вы возможность перебрать все танки, чтобы увидеть, находятся ли они в пределах досягаемости, а не все объекты в пределах досягаемости, чтобы увидеть, являются ли они танками? Похоже, это сэкономит вам много времени как на итерации, так и на вызовах instanceof...

person Kyle Sletten    schedule 19.04.2011

В общем, полиморфизм — это способ избежать ненужного оператора instanceof.

person Michał Šrajer    schedule 19.04.2011

Я не знаю, нужно ли вам использовать Visitor для обработки этого поведения. Ваш случай может быть очень легко выполнен с использованием общего полиморфизма. Сначала я хотел предложить фабричный метод и переменную типа, но решение могло быть еще проще.

Итак, у вас есть общий абстрактный суперкласс. (Организация). Таким образом, в этом классе вы можете определить метод с именем hitByMissile() (или любой другой). В вашем классе танков вы можете сделать hitByMissile, работающим иначе, чем, скажем, Obstacle. Ваш код не должен решать, как вести себя каждый объект. Поведение должно определяться самим объектом. Таким образом, вы можете просто перебирать объекты и вызывать метод.

person uncaught_exceptions    schedule 19.04.2011
comment
У меня есть привычка начинать все свои ответы с оговорки, что я мог не понять проблему :). Это чтобы избежать общего снобизма, но прокомментируйте меня, если решение не сработает для вас. - person uncaught_exceptions; 19.04.2011