Предложение по шаблону для использования изделия из инвентаря
К сожалению, я занимаюсь созданием приключенческой игры на java и slick2d из-за проекта объектно-ориентированной разработки программного обеспечения в моей школе.
Я применяю архитектуру MVC и создаю Предметы, читая файл карты Tiled в классе ItemFactory. Кажется, они подходят для такой игры.
В игре есть несколько предметов, которые влияют на игру и особенно на персонажа. (персонаж такой же, как игрок в этом контексте)
Существует модель персонажа, и она имеет внутренний класс Inventory, который в основном состоит из ArrayList элементов.
Контроллеры персонажей имеют функцию, которая называется takeItem, которая принимает аргумент контейнера (то есть полки), который также имеет ArrayList элементов, а затем удаляет то, что внутри, и помещает в инвентарь.
И у него есть функция, которая называется useItem, принимает индекс int и использует элемент в соответствующем индексе.
До этого все нормально.
Теперь главная проблема заключается в том, что эти предметы имеют конкретное применение. Например, зелье здоровья увеличивает здоровье персонажа, зелье невидимости делает его невидимым и т. Д.
Чтобы поддерживать хороший дизайн, как и где я могу интерпретировать и прикреплять функции к этим элементам?
Пока что я думаю что-то вроде этого: 1- Я не добавил ни одной функции в класс элементов. Он имеет только имя, описание и Enumerated ItemType и int power 2- я выполняю каждую работу в методе useItem() контроллера персонажа, т.е.
if (itemBeingUsed.getType() == Common.ItemType.POTION)
`characterModel.increaseHealth(itemBeingUsed.getPower())`
Как видите, это кажется странным, потому что контроллер персонажей действует как интерпретатор элементов.
3. Другой подход может заключаться в создании класса ItemInterpreter, который принимает контроллер персонажа. Хотя это кажется более элегантным, я заставлю меня выполнять кучу ненужных функций в контроллере персонажей, например, функцию "increment CharacterHealth", которая вызывает функцию приближения в модели персонажа и так далее.
Другая возможная проблема заключается в том, что я использую функции Character Controller для использования предметов, но предметы могут также влиять на игру, то есть замедлять время, но Character Controller находится ниже иерархии классов игры и не имеет доступа к функциям Game/GameController. Замедление игрового времени и т. Д., По-видимому, сейчас не так, так что похоже, что я могу использовать Предметы в классе CharacterController, но я хотел бы узнать ваше мнение, если это хороший дизайн или что может быть лучше.
Некоторые коды для устранения проблем: (примечание: они не завершены) Примечание: я поместил перечисленные типы, такие как ItemType, Direction и т. Д., В общий класс, общий для всех классов.
Используйте функцию элемента в CharacterController:
public void useItem(int index){
Item item = ((Character)obj).inventory.takeItem(index);
if (item != null) {
if (item.getType() == Common.ItemType.POTION) {
((Character)obj).increaseHealth(item.getPower()); // I have extended a Person class, now I have to typecast to character everytime, which is also nonsense
}
else
System.out.println("and other items..");
}
else
System.out.println("Cannot use");
}
Я не поместил весь код, но я могу, если я не был ясен в объяснении.
2 ответа
Это классический случай, когда интерфейсы, наследование и полиморфизм вступают в игру. Всякий раз, когда вы используете switch
или последовательность if
тогда вы должны рассмотреть полиморфизм.
В этом случае определите интерфейс или абстрактный класс для используемого элемента.
public interface Useable {
public void useItem(Person personUsing);
}
Теперь каждый элемент просто реализует useable, и вы можете просто проверить, является ли элемент пригодным для использования, отображать ли возможность его использования и вызывать метод при его использовании.
Как разработчик SOA (сервис-ориентированная архитектура), я не стал бы добавлять логику к своим элементам, потому что в таком случае становится очень сложно факторизовать и рационализировать мой код. Кроме того, помните, что композиция гораздо лучше, чем наследование, если вы хотите масштабируемый код.
public interface ItemInterface {
public String getName();
public String[] getCharacteristics();
}
Я бы определил некоторые эффекты элементов, обрабатывающие логику:
public interface ItemEffectInterface {
public String getName();
public void affectCharacter(ItemInterface item, CharacterInterface character, parameters);
public void affectInventory(ItemInterface item, InventoryInterface inventory, parameters);
public void affectGame(ItemInterface item, GameInterface game, parameters);
}
Я использую интерфейсы для всех своих объектов, чтобы создать слабую связь между моими классами (это обеспечивает более масштабируемый, обслуживаемый и тестируемый код).
Вы можете определить абстрактный класс AbstractItemEffect
если вы хотите определить поведение по умолчанию "ничего не делать" в эффектах элемента расширителя для всех методов и просто определить активные методы для каждого эффекта.
Затем, если вы хотите динамически настраиваемую игру, вы можете определить взаимодействие элемента и эффектов в файле / базе данных. Вот пример JSON:
[
{
"item": "health potion",
"effect": "heal",
"parameters": {
"hp": 100
}
},
{
"item": "mana potion",
"effect": "regen",
"parameters": {
"mp": 150
}
},
{
"item": "mana potion",
"effect": "drunk",
"parameters": {
"duration": 1000
}
},
...
[
Затем используйте класс обслуживания, чтобы сопоставить эффект с элементами при использовании:
public class ItemUser {
private Map itemEffects = new HashMap();
// Call it at service initialization after interpreting the configuration
// (configuration is optional, you can call it directly of course).
public void addItemEffect(ItemInterface item, ItemEffectInterface itemEffect, parameters) {
Map effect = new HashMap();
effect.push("effect", itemEffect);
effect.pust("parameters", parameters);
this.itemEffects.put(item.getName(), effect);
}
// Call it when you want to use an item.
public void useItem(ItemInterface item, Character character, GameInterface game, InventoryInterface inventory) {
Map itemEffects = this.itemEffect[item.getName()];
// Process all the effects attached to your item
// on game, character, inventory, ...
}
}
С помощью этой архитектуры вы можете прикрепить эффект ко многим элементам, а элемент ко многим эффектам. Это должно помешать вам делать дубликаты кода.
В то же время каждый элемент и эффект элемента будут обрабатывать свою собственную сложность и не будут делать всю систему более сложной. Это не относится к шаблону "случай переключения", подобному тому, который вы описали в 2.
, Представьте свой класс, если у вас есть 100 эффектов и 200 предметов...
Извините за мой плохой / неполный код JAVA!