Страницы

Поиск по вопросам

среда, 26 февраля 2020 г.

Полиморфная функция должна выполнить определенное действие в зависимости от того, объект какого подкласса в нее передали

#ооп #разработка_игр #полиморфизм


Делаю игру. Есть общий класс Creature, от него наследуются Monster и Human. Есть
класс Tile (игровая клетка), от нее наследуются DivineTrap (божественная ловушка) и
DivineWall (божественная стена). 

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

У нас есть 4 разных поведения: 


Monster не может пройти на Tile, если он типа DivineWall, а если это DivineTrap,
то монстр, передвинувшись на него, погибает. 
Human может проходить на DivineWall и DivineTrap беспрепятственно. 


Собственно, подобрались к проблеме: если бы у нас был только один тип существа, к
примеру, Monster, мы могли бы воспользоваться полиморфизмом и позволить клетке в зависимости
от ее типа обработать ход существа. Т.е. если клетка - ловушка, она позволяет монстру
пройти на себя, а потом убивает его. Если клетка - стена, она не пропускает монстра.
Но, поскольку у нас два вида существа (Human и Monster), то клетка должна учитывать
тип существа, которое пытается зайти на эту клетку. Если же клетка позволит существу
самостоятельно обработать эту ситуацию, то получается замкнутый круг: ведь существо
должно знать тип клетки, на которую оно ступает.

Понятно, что можно прервать замкнутый круг, задействовав instanseof. Или опросить
состояние объекта. Но мне хотелось бы знать, как называется моя проблема в ООП, и каковы
методы ее решения. Потому что я не уверен, что instanceof и опрос состояния наилучшие
решения.
    


Ответы

Ответ 1



Ваша проблема кроется в нарушении, как минимум принципа единственной ответственности. Клетка не должна позволять или запрещать проходить на себя, пропускать людей и убивать монстров. Клетка, если можно так выразиться, объект-справочник. Она должна знать свои координаты на карте и то, что на ней находится (некий объект-сущность: игрок, монстр, стена, ловушка, ресурсы.. что угодно) и предоставлять эту информацию по требованию других объектов (например, при поиске кратчайшего пути опрашиваем все соседние клетки и анализируем, куда лучше пойти). Если Вы будете добавлять в "клетку" логику, это будет смешиванием ответственностей. Далее: ... логичнее было бы вместе наследования применить композицию (ловушку или стену сделать, как поле игровой клетки)... Это было бы не просто логичнее, наследование здесь семантически некорректно. Ведь если Вы моделируете рабочее место пользователя, вы же не будете монитор, клавиатуру и мышь наследовать от стола только потому, что эти объекты находятся на столе. Ведь так? С ловушкой или стеной, которая находится на клетке, похожая ситуация. Поэтому ловушка должна быть объектом на поле, но не полем. Ближе к проблеме.. не думаю, что у этой проблемы есть какое-то конкретное название и конкретное решение. Это просто проблема проектирования, и методы ее решения зависят от того, чего вы хотите добиться своей программой. Что касается варианта решения, согласен с @Кирилл Малышев по поводу паттерна посетитель. Хотя с другой стороны эту логику можно также реализовать в классах Monster и Human, сделав, например, метод die (умереть) защищенным и вызывать его в случае, когда хп становится равным 0 (в данном случае я имею ввиду эти классы базовыми для конкретных типов монстров и людей). Ловушки же в свою очередь будут только снимать хп. Классы людей могут иметь иммунитет к божественным ловушкам, а монстры - к демоническим. Реализовав такую логику, можно запросто добавить босса, например, с иммунитетом к любым ловушкам. Все зависит от того, насколько детально Вам нужно проработать проект.

Ответ 2



Хороший вопрос. Я не знаю как называется в теории данный паттерн, но я бы решал это таким образом: У нас есть три сущности: Клетка Creature Реакция В соответствии с этим и нужно строить структуру классов. 1 и 2 уже расписаны в вопросе. Остался базовый класс "Реакция" В конструктор должны быть переданы объекты 1 и 2. В зависимости от сочетания типов этих двух объектов создается нужный экземпляр потомка от базового класса Реакция. Ну и потомок реализует уже необходимое действие.

Комментариев нет:

Отправить комментарий