2011-08-22 4 views
4

이것은 광범위한 질문입니다. 그러나 제 자신의 이해를 위해 나는 그것을 버릴 것이라고 결심했습니다.서로를 알고있는 객체들의 집합

누구나 서로를 알고있는 다양한 물건을 원할 때 디자인을 추천 할 수 있습니까?

인물/커뮤니티 시뮬레이션을 비유로 사용하려면 X가 동적으로 커지고 축소 될 수있는 X 인물 개체가있을 때 적용 할 가장 좋은 패턴은 무엇이며, 각 인물 개체는 다른 물건들?

프로그래밍 구문을 고려하지 않고 각 사람의 관계 (좋아요, 싫어함, 만난적 없음 등)를 나타내는 X 눈금이 X 일 수 있습니다. 기본적으로 코드에서 새로운 개체가 생성 될 때마다 업데이트되는 각 개체 개체와는 별도의 개체로 코드를 구현할 것을 생각했지만 완전히 우아하지 않은 솔루션처럼 보였습니다. 누구든지 여기에 조언이 있습니까?

둘째로, "인벤토리"가있는 사람 또는 가지고 다니는 항목이있는 경우 각 사람의 인벤토리를 클래스의 목록 멤버로 만들 수 있다고 생각했습니다. 이 연결된 목록은 사람이 성장하고 수축하는 대상으로 성장하고 줄어들 것입니다.

사람 개체는 자신이 가지고있는 개체에 대해 쉽게 쿼리 할 수 ​​있지만 그 반대의 경우도 가능한 효율적인 방법을 찾을 수는 없습니다. 즉, 항목을 쿼리하고 해당 항목이있는 사람들을 찾을 수 있기를 원합니다.

나는이 Q-A를 읽었습니다. Game Objects Talking To Each Other .. 그러나 그것이 내 경우에 완전히 적용되는지 확실하지 않습니다.

누구든지 조언을 해줄 수 있습니까?

감사합니다.

- r

+0

사람 -> 개체/개체 -> 사람 성능 문제에 관해서. 왜 각 객체에 대한 사람들의 링크 된 목록을 가지고 있지 않습니까? 따라서 특정 객체를 가진 사람을 확인하려면 해당 객체의 목록을 반복하면됩니다. 이것은 개인의 개체 목록이 업데이트되는 것과 동시에 업데이트 될 수 있습니다. 스파 스 매트릭스 아이디어에서 스파 스 그래프를 사용할 수 있습니다. 누구를 아는 사람인지 알아 보려면 연결된 구성 요소 알고리즘을 사용하십시오. http://en.wikipedia.org/wiki/Connected_component_(graph_theory) – user885074

+0

메모리 내 데이터베이스 - http://en.wikipedia.org/wiki/In- memory_database – selbie

+0

[옵서버] (https://secure.wikimedia.org/wikipedia/en/wiki/Observer_pattern)가 도움이 될 수 있습니다. – cmannett85

답변

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

, I 클래스 "관리자"를 호출 싫어하지만. 당신이 관계의 많은이 방법을 모델링해야하는 경우

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

이 목적으로 관계형 데이터베이스가 정확하게 작성되었습니다. 당신이 그것에 익숙하지 않다면, 그것에 대해 읽어보십시오!

여러 테이블이있는 데이터베이스를 사용할 수 있습니다. 하나의 테이블은 사람의 기본 속성과 ID를 보유하는 "사람"입니다.

"met"와 같은 관계를 모델링하려면 두 개의 열이있는 met이라는 테이블을 만듭니다.이 테이블에는 서로 만난 두 사람의 ID가 저장됩니다.

데이터베이스는 "John을 만난 모든 사람 찾기"와 같은 쿼리에 대해 많이 최적화되어 있습니다. 클라이언트/서버 데이터베이스를 설치하지 않으려면 SQLite

0

과 같은 파일 기반 데이터베이스를 사용할 수 있습니다. GOF 책의 표준 예제는 Mediator 패턴을 사용하는 것입니다. 그들이 제공하는 예제는 GUI 창/대화 상자 (Mediator)가 해당 컨트롤 (때로는 동료라고 함)을 추적하고 윈도우 크기 조정과 같은 관련 이벤트의 컨트롤을 알리는 것입니다.이 예제는 컨트롤 중재자에게 다른 통제에 대한 정보를 묻습니다.

개체가 개체의 특정 인스턴스를 추적 할 수있게하려면 Observer 패턴 (cbamber85로 표시)이 유용합니다. 그러나 일반 개체의 인구에서 발생하는 이벤트에 대한 정보를, 당신은 중재자를 사용하는 것이 좋습니다.

특정 문제와 관련하여 중재자가 일반 인구수의 변화 (성장 또는 감소)를 추적하는 데 도움을 줄 수 있습니다.

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로 구조체를 저장 한 경우 순환 의존성으로 인해 고아 사이클 문제가 발생할 수 있음).

사실 모든 사람이 제한없이 다른 사람을 볼 수있게하려면 동료 클래스에 프록시를 저장하지 않아도되고 관리자 클래스가 우선적으로 사용됩니다. 그런 다음 모든 통신은 프록시 콜렉션과 같은 역할을하는 관리자를 통해 발생합니다. 이러한 경우 동료의 평생을 관리하는 동일한 관리자가 프록시 관리자로도 역할을 수행하여 데이터의 중복을 방지하고 액세스를 최적화 할 수 있습니다.

관련 문제