Коллекции предметов, которые знают друг друга - PullRequest
4 голосов
/ 22 августа 2011

Это довольно широкий вопрос, но для собственного понимания я решил, что я его выброшу.

Может ли кто-нибудь порекомендовать проекты или, может быть, даже общепринятые шаблоны проектирования для ситуаций, когда нужно, чтобы различные объекты знали друг о друге?

Чтобы использовать симуляцию людей / сообществ в качестве аналогии, какой шаблон лучше всего применять, если у вас есть объекты X-людей, в которых X может динамически расти / уменьшаться, и когда каждый объект-человек придерживается мнения или отношения с другим объекты?

Думая вне синтаксиса программирования, я мог бы просто иметь сетку X за X, которая представляет отношения каждого человека друг с другом (любит, не любит, не встречал и т. Д.). Я думал о том, чтобы в основном реализовать это в коде как отдельный объект от каждого объекта-человека, который обновлялся каждый раз, когда создавался новый, но это казалось совершенно не элегантным решением. У кого-нибудь есть здесь советы?

Во-вторых, если у меня есть коллекции людей, у каждого из которых есть «запасы» или предметы, которые они несут, я подумал, что мог бы создать инвентарь каждого человека в качестве члена Списка класса; этот связанный список будет расти и уменьшаться по мере роста и уменьшения объектов, которые есть у человека.

К объектам людей можно было бы легко обращаться за их объектами, которые они несут, но я не могу найти эффективный способ сделать возможным и наоборот; то есть я хочу иметь возможность запросить элемент и выяснить, у каких людей этот элемент есть.

Я читал этот вопрос: Игровые объекты, говорящие друг с другом ... но я не уверен, что это полностью применимо к моему делу.

Может кто-нибудь дать совет?

Спасибо!

- Р

Ответы [ 4 ]

1 голос
/ 22 августа 2011

То, что вы ищете, является относительно простой ассоциацией.

class Person {
    struct Relationship {
        // whatever
    };
    std::map<Person*, Relationship> relationships;
public:
    Person() {}
    void Meet(Person* other) {
        // think about it
        relationships[other] = ...;
    }
    ~Person() {
        // Loop through the map and ensure no dud relationships
        for(auto& pair : relationships) {
            pair.first->relationships.erase(this);  
        }
    }
};

Что касается определения человека, у которого есть определенные предметы, я думаю, что (относительно) простое решение для менеджера должно работать хорошо, хотя я ненавижу называть классы "Менеджер".

class PersonManager {
    struct Item { ... };
    std::set<Item, std::set<Person*>> items; // set for no dupes
public:
    void AddToInventory(Item i, Person* p) {
        items[i].insert(p);
    }
    std::vector<Person*> PeopleWithItem(Item i) {
        return std::vector<Person*>(items[i].begin(), items[i].end());
    }
};

Если вам нужно смоделировать лот отношений таким образом, я бы предложил пойти в базу данных.Однако я сомневаюсь, что любая база данных может соответствовать эффективности этого кода - вы даже можете перейти к хеш-контейнерам и получить O (1) для большинства из этих действий.

1 голос
/ 22 августа 2011

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

Итак, у вас есть класс

class ColleagueProxy
{
private:
    // a pointer to the Colleague
    // it can be a naked pointer since it does not deal with lifetime
    Colleague friend_;

    // a callback for when death comes to the colleague
    typedef std::function<void (ColleagueProxy *)> DeathHandler;
    DeathHandler deathCallback_;

    // an identifier for friends to know me by
    std::string id_;

public:
    // ctor takes a colleague and a callback for when colleague dies
    // ctor notifies friend of new proxy following - in case that is useful info
    ColleagueProxy(Colleague * friend, DeathHandler callback, std::string const& myName) :
      friend_(friend),
      deathCallback_(callback)
    {
      if (friend)
        friend_->proxyConnecting(this);
    }

    // dtor may notify friend as well
    ~ColleagueProxy()
    {
      if (friend)
        friend_->proxyLeaving(this);
    }

    // the communication interface
    void sayHi() 
    { 
      if (friend)
        friend_->sayHi(this); 
    }
    // ...

    // my name badge
    std::string id() { return id_; }

    // a way for the friend to say goodbye
    void Goodbye()
    {
       deathCallback_(this);
    }
};

и тогда коллеги будут хранить эти коммуникационные прокси

class Colleague
{
private:
    std::map<std::string, std::shared_ptr<ColleagueProxy>> friends_;
    std::vector<std::shared_ptr<ColleagueProxy>> colleaguesWhoConsiderMeAFriend_;

    void GoodbyeCallback(ColleagueProxy * that)
    {
      // search through friends_ and remove that
    }
}

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

Смысл такого типа конструкции заключается в том, чтобы предоставить механизм связи, который можно настраивать для каждого соединения (каждый прокси-объект может содержать состояние, специфичное для соединения). Это может быть применено к мнениям (как в оригинальном вопросе) и многим другим вещам. Вам не нужно беспокоиться о циклических зависимостях, потому что управление временем жизни не является частью соединения (например, если вы просто сохранили структуру с shared_ptr для друга, где циклические зависимости могут вызывать проблемы потерянных циклов).

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

1 голос
/ 22 августа 2011

Реляционные базы данных были созданы именно для этой цели.Если вы не знакомы с ним, прочитайте об этом!

Вы можете использовать базу данных с несколькими таблицами.Одной таблицей будет «персона», которая содержит основные свойства людей плюс идентификатор.

Чтобы смоделировать отношения типа «встретились», создайте таблицу с именем met с двумя столбцами, которые содержат идентификаторы двух людей, которыевстретились друг с другом.

Базы данных сильно оптимизированы для таких запросов, как «найти всех, кто встречался с Джоном».Если вы не хотите устанавливать клиент-серверную базу данных, вы можете использовать файловую базу данных, такую ​​как SQLite

0 голосов
/ 22 августа 2011

Каноническим примером в книге GOF является использование шаблона Mediator .Пример, который они приводят, заключается в том, что окно / диалоговое окно графического интерфейса пользователя (посредник) отслеживает свои элементы управления (иногда называемые коллегами) и уведомляет элементы управления соответствующими событиями, такими как изменение размера окна и т. Д. Я полагаю, что этот пример расширяет возможности элементов управлениязапросить у посредника информацию о других элементах управления.

Если вы хотите, чтобы ваши объекты могли отслеживать конкретные экземпляры объектов, полезен шаблон Observer (как указано cbamber85).Но для общей информации о событиях, происходящих в совокупности объектов , вы можете рассмотреть возможность использования посредника.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...