彼此了解的对象集合

时间:2011-08-22 06:48:30

标签: c++ design-patterns

这是一个广泛的问题,但根据我自己的理解,我决定将它扔出去。

任何人都可以推荐设计,或者甚至是普遍接受的设计模式,以便人们想要彼此了解的各种物体吗?

要使用人/社区模拟作为类比,当您拥有X可以动态增长/收缩的X对象并让每个人对象持有其他人的意见或关系时,应用的最佳模式是什么对象

在编程语法之外思考,我可以只有一个X×X网格来表示每个人彼此之间的关系(喜欢,不喜欢,没有遇到等等)。我想基本上在代码中实现它作为每个人对象的单独对象,每次创建新对象时都会更新,但它似乎是一个完全不优雅的解决方案。有没有人在这里有任何建议?

其次,如果我收集的每个人都有“库存”或他们携带的物品,我认为我可以创建每个人的库存作为班级的List成员;这个链表会随着人们成长和缩小的对象而增长和缩小。

人们可以很容易地查询人物对象所携带的物体,但我无法找到一种有效的方法来使反之亦然;也就是说,我希望能够查询项目并找到哪些人拥有该项目。

我确实读过这个Q-A: Game Objects Talking To Each Other ..但我不确定它是否完全适用于我的情况。

有人可以提供任何建议吗?

谢谢!

- R的

4 个答案:

答案 0 :(得分:1)

完全为此目的制作了关系数据库。如果你不熟悉它,请阅读它!

您可以使用包含多个表的数据库。一张桌子就是“人”,它拥有人的基本属性和身份证。

要为“met”之类的关系建模,请创建一个名为met的表格,其中包含2列,其中包含两个相互认识的人的ID。

数据库针对诸如“找到遇到约翰的所有人”之类的查询进行了大量优化。如果您不想安装客户端/服务器数据库,可以使用基于文件的数据库,如SQLite

答案 1 :(得分:1)

如果这种关系只是沟通,而不是实际的终身所有权,并且你不希望每个人都能够自动与所有人交谈,那么接口代理通常是一个良好的开端。这些只是中间类,它们拥有与同事沟通的方式,并在任何一方的生命周期发生变化时提供通知。这些通常是单向的,但是当你配对它们并添加更多的胶水代码时,它们就会变成双向的。

所以,你有一个班级

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类中存储Proxies,而是首选经理类。然后所有通信都通过管理器进行,管理器就像代理集合一样。在这些情况下,管理同事生命周期的同一经理可能也扮演代理经理的角色,以防止重复数据和优化访问。

答案 2 :(得分:1)

您正在寻找的是一种相对简单的关联。

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());
    }
};

如果你必须以这种方式建立 lot 关系的模型,我建议你去一个数据库。但是,我怀疑任何数据库都可以匹配此代码的效率 - 您甚至可以使用哈希容器并为大多数这些操作获取O(1)。

答案 3 :(得分:0)

GOF书中的规范示例是使用Mediator模式。他们提供的示例是GUI窗口/对话框(Mediator)跟踪其控件(有时称为同事),并通知相关事件的控件,例如窗口大小调整等。我相信示例扩展到允许控件查询介体以获取有关其他控件的信息。

如果希望对象能够跟踪特定的对象实例,Observer模式(如cbamber85所示)非常有用。但是对于一般有关对象群体中发生的事件的信息,您可能需要考虑使用Mediator。

关于您的具体问题,调解员可能有助于跟踪一般人群的变化(增长或萎缩)。