Если отношения - это просто общение, а не пожизненное владение, и вы не хотите, чтобы все могли автоматически общаться со всеми, тогда интерфейсные прокси обычно являются хорошим началом. Это просто промежуточные классы, которые владеют способом общения с коллегой и предоставляют уведомления при изменении времени жизни любой из сторон. Обычно они однонаправлены, но когда вы соединяете их и добавляете немного больше связующего кода, они становятся двунаправленными.
Итак, у вас есть класс
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 не является необходимым, и предпочтительным является класс менеджера. Тогда все коммуникации происходят через менеджера, который действует как коллекция прокси. В этих случаях, вероятно, тот же менеджер, который управляет временем жизни Коллег, может также выполнять роль диспетчера прокси, чтобы предотвратить дублирование данных и оптимизировать доступ.