Инвентарь
От: Sorc17 Россия  
Дата: 09.08.11 13:10
Оценка:
Всем привет. Решил сделать такую простенькую вещь как инвентарь предметов игрока для своей игрушки. Но внезапно на толкнулся на непреодолимые без костылей проблемы. Постараюсь как можно короче изложить саму суть.

Не получается:

1) Написать метод добавления абстрактного предмета
2) Хранить предметы в адекватной коллекции

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

Вот самый минимальны пример, чтобы проиллюстрировать.

Предметы:
abstract class Item {
    int id;
}

class Gold extends Item {
    int count;

    void addCount(int count) {
        this.count += count;
    }
}
class Rune extends Item {
}


Игрок и инвентарь:
class Inventory {
    List<Item> items;

    void addItem(Item i) {
       items.add(i);
    }

    Item getItem() {
        // Как получить конкретный предмет из абстрактной коллекции?
    }
}

class Player {
    Inventory inv;

    void addItem(Item i) {
        // Если добавляем золото, то надо из инвентаря получить золото и увеличить его количество.
        // Если добавляем руну, то нужно просто добавить её в инвентарь.
        // Но как, если мы не знаем что такое i?
    }
}


Думал над двумя вариантами. Первый: добавить в предметы тип, чтобы можно было сделать switch() по типу предмета в addItem и поиск по предметам:
class Gold extends Item {
    int type = ItemTypes::GOLD;
    int count;

    void addCount(int count) {
        this.count += count;
    }
}
class Rune extends Item {
    int type = ItemTypes::RUNE;
}

class Inventory {
    List<Item> items;

    void addItem(Item i) {
       items.add(i);
    }

    Item getItem(int type) {
        foreach (Item i in items) {
            if (i.type == type) {
                return i;
            }
        }
    }
}

class Player {
    Inventory inv;

    void addItem(Item i) {
        switch(i.type) {
            case ItemTypes::GOLD:
                inv.getItem(i.type).addCount(i.count);
                break;
            case ItemTypes::RUNE:
                inv.addItem(i);
                break;
        }
    }
}


А второй вариант ещё хуже. это разнести этот switch() на методы:
class Player {
    Inventory inv;

    void addGold(Gold g) {
        inv.getItem(ItemTypes::GOLD).addCount(g.count);
    }
    void addRune(Rune r) {
        inv.addItem(r);
    }
}


В итоге не знаю как поступить. Сохранять в предмете его тип это вообще не очень. Как и плодить методы для каждого типа.
Для нас [Thompson, Rob Pike, Robert Griesemer] это было просто исследование. Мы собрались вместе и решили, что ненавидим C++ [смех].
Re: Инвентарь
От: Sorc17 Россия  
Дата: 09.08.11 13:40
Оценка:
Интересно, а как ООП смотрит на вот такой вариант:
abstract class Item {
    int id;
}
class Gold extends Item {
    int count;

    void addCount(int count) {
        this.count += count;
    }
}
class Rune extends Item {
}

class Inventory {
    HashMap<int, ArrayList<Item>> map;

    void addItem(int type, Item i) {
        ArrayList<Item> items = map.get(type);
        if (items == null) {
            ArrayList<Item> new_items = new ArrayList<Item>();
            new_items.add(i);
            map.put(type, new_items);
        } else {
            items.add(i);
        }
    }

    ArrayList<Item> getItem(int type) {
        return map.get(type);
    }
}

class Player {
    Inventory inv;

    void addItem(int type, Item i) {
        switch(i.type) {
            case ItemTypes::GOLD:
                inv.getItem(ItemTypes::GOLD)[0].addCount(i.count);
                break;
            case ItemTypes::RUNE:
                inv.addItem(ItemTypes::RUNE, i);
                break;
        }
    }
}
Для нас [Thompson, Rob Pike, Robert Griesemer] это было просто исследование. Мы собрались вместе и решили, что ненавидим C++ [смех].
Re: Инвентарь
От: Буравчик Россия  
Дата: 09.08.11 14:16
Оценка: 2 (1)
Здравствуйте, Sorc17, Вы писали:


class Player {
    Inventory inv;

    void addItem(Item i) {
        // Если добавляем золото, то надо из инвентаря получить золото и увеличить его количество.
        // Если добавляем руну, то нужно просто добавить её в инвентарь.
        // Но как, если мы не знаем что такое i?
    }
}


Это и есть ответ на вопрос.
Ведь если inventory не знает, как добавлять элементы к себе, то надо спросить об этом у самого элемента.

Все классы, которые хотят сохраняться в inventory должны реализовать интерфейс Storable (метод addToInventory).
Inventory при добавлении элемента вызывает этот метод.

interface Storable {
    void addToInventory(Inventory);
}

class Gold implements Storable {
    void addToInventory(Inventory inv) {
        получить золото в inv
        увеличить его количество в inv
    }
}

class Inventory {
    Storable getItem(...)
    void addItem(Storable item)
}

class Player {
    Inventory inv;

    void addItem(Item i) {
            inv(i);
    }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Best regards, Буравчик
Re: Инвентарь
От: Centaur Россия  
Дата: 09.08.11 14:22
Оценка:
Здравствуйте, Sorc17, Вы писали:

S>    void addItem(Item i) {
S>        // Если добавляем золото, то надо из инвентаря получить золото и увеличить его количество.
S>        // Если добавляем руну, то нужно просто добавить её в инвентарь.
S>        // Но как, если мы не знаем что такое i?
S>    }

Нужный паттерн называется Double Dispatch. Он, кстати, ещё дальше понадобится, когда будешь делать тыкание предметами в персонажей.
Re[2]: Инвентарь
От: Sorc17 Россия  
Дата: 09.08.11 14:32
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Все классы, которые хотят сохраняться в inventory должны реализовать интерфейс Storable (метод addToInventory).

Б>Inventory при добавлении элемента вызывает этот метод.

Точно! И как мне сразу не пришло это в голову. Ведь я уже встречал такой код и не раз. Спасибо
Для нас [Thompson, Rob Pike, Robert Griesemer] это было просто исследование. Мы собрались вместе и решили, что ненавидим C++ [смех].
Re[2]: Инвентарь
От: Sorc17 Россия  
Дата: 09.08.11 14:33
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Нужный паттерн называется Double Dispatch. Он, кстати, ещё дальше понадобится, когда будешь делать тыкание предметами в персонажей.


Ага, про Double Dispatch мне уже намекнули. Ну у меня он пока не дабл в случае инвентаря, ведь предмету-то всё равно в какой мешок лезть (по крайне мере пока), но зато после вашего поста я уже знаю где он понадобится
Для нас [Thompson, Rob Pike, Robert Griesemer] это было просто исследование. Мы собрались вместе и решили, что ненавидим C++ [смех].
Re[3]: Инвентарь
От: Centaur Россия  
Дата: 11.08.11 05:23
Оценка:
Здравствуйте, Sorc17, Вы писали:

C>>Нужный паттерн называется Double Dispatch. Он, кстати, ещё дальше понадобится, когда будешь делать тыкание предметами в персонажей.


S>Ага, про Double Dispatch мне уже намекнули. Ну у меня он пока не дабл в случае инвентаря, ведь предмету-то всё равно в какой мешок лезть (по крайне мере пока), но зато после вашего поста я уже знаю где он понадобится


Предмету всё равно в какой мешок, но ему как-то придётся ориентироваться в содержимом мешка. Где содержимое мешка — опять-таки куча разнотипных объектов, которые ничего друг о друге знать не хотят.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.