Предложение по шаблону для использования изделия из инвентаря

К сожалению, я занимаюсь созданием приключенческой игры на 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!

Другие вопросы по тегам