Допустим, у меня есть два типа объектов, A и B. Отношения между ними многие-ко-многим, но ни один из них не является владельцем другого.
И экземпляры A и B должны знать о соединении; это не только один путь.
Итак, мы можем сделать это:
class A
{
...
private: std::vector<B *> Bs;
}
class B
{
private: std::vector<A *> As;
}
У меня вопрос: куда мне поместить функции для создания и уничтожения соединений?
Должно ли это быть A :: Attach (B), которое затем обновляет векторы A :: Bs и B :: As?
Или это должно быть B :: Attach (A), что кажется одинаково разумным.
Ни один из тех, кто чувствует себя хорошо. Если я перестану работать с кодом и вернусь через неделю, я уверен, что не смогу вспомнить, нужно ли мне делать A.Attach (B) или B.Attach (A).
Возможно, это должна быть такая функция:
CreateConnection(A, B);
Но создание глобальной функции также нежелательно, учитывая, что эта функция предназначена специально для работы только с классами A и B.
Другой вопрос: если я часто сталкиваюсь с этой проблемой / требованием, могу ли я как-то найти общее решение? Возможно, класс TwoWayConnection, который я могу извлечь или использовать в классах, которые разделяют этот тип отношений?
Каковы некоторые хорошие способы справиться с этой ситуацией ... Я знаю, как справиться с ситуацией «один-ко-многим» владеет D), но этот хитрее.
Изменить: просто чтобы сделать его более явным, этот вопрос не включает в себя вопросы собственности. И A, и B принадлежат некоторому другому объекту Z, а Z решает все вопросы, связанные с владением. Меня интересует только то, как создать / удалить связи «многие ко многим» между А и В.
Pointer
и естьGestureRecognizer
. Указатели принадлежат и управляются классом InputManager. GestureRecognizer принадлежат экземплярам Widget, которые в свою очередь принадлежат экземпляру Screen, который принадлежит экземпляру приложения. Указатели присваиваются GestureRecognizer, чтобы они могли передавать им необработанные входные данные, но GestureRecognizers должны знать, сколько указателей в настоящее время связано с ними (чтобы различать жесты 1 палец против 2 пальцев и т. Д.).Ответы:
Одним из способов является добавление открытого
Attach()
метода, а также защищенногоAttachWithoutReciprocating()
метода для каждого класса. ЗаведитеA
иB
общих друзей, чтобы ихAttach()
методы могли вызывать другиеAttachWithoutReciprocating()
:Если вы реализуете подобные методы для
B
вас, вам не нужно будет помнить, какой класс вызыватьAttach()
.Я уверен, что вы могли бы обернуть это поведение в
MutuallyAttachable
классе, который унаследованA
иB
унаследован, что позволит вам избежать повторения и набирать бонусные очки в Судный день. Но даже простой подход «реализуй это в обоих местах» сделает работу.источник
MutuallyAttachable
класс, который я написал для этой задачи, если кто-то хочет использовать его повторно: goo.gl/VY9RB (Pastebin) Редактировать: Этот код можно улучшить, сделав его шаблоном класса.MutuallyAttachable<T, U>
шаблон класса в Gist. gist.github.com/3308058Возможно ли, чтобы сами отношения имели дополнительные свойства?
Если так, то это должен быть отдельный класс.
Если нет, то будет достаточно любого механизма управления списком возвратно-поступательного движения.
источник
Обычно, когда я попадаю в такие ситуации, которые чувствуют себя неловко, но я не могу точно сказать, почему, потому что я пытаюсь положить что-то не в тот класс. Почти всегда есть способ вывести некоторую функциональность из класса
A
иB
прояснить ситуацию.Одним из кандидатов на перемещение ассоциации является код, который создает ассоциацию в первую очередь. Может быть, вместо того, чтобы звонить,
attach()
он просто хранит списокstd::pair<A, B>
. Если есть несколько мест, создающих ассоциации, его можно абстрагировать в один класс.Другим кандидатом на перемещение является объект, которому принадлежат связанные объекты,
Z
в вашем случае.Другим распространенным кандидатом на перенос ассоциации является код, который часто вызывает методы
A
илиB
объекты. Например, вместо вызоваa->foo()
, который внутренне что-то делает со всеми связаннымиB
объектами, он уже знает ассоциации и вызываетa->foo(b)
для каждого. Опять же, если его называют несколько мест, его можно абстрагировать в один класс.Много раз объекты, которые создают, владеют и используют ассоциацию, оказываются одним и тем же объектом. Это может сделать рефакторинг очень простым.
Последний метод заключается в получении отношения от других отношений. Например, братья и сестры являются отношениями «многие ко многим», но вместо того, чтобы хранить список сестер в классе братьев и наоборот, вы выводите эти отношения из родительских отношений. Другое преимущество этого метода заключается в том, что он создает единый источник правды. Нет никакого возможного способа для ошибки или ошибки времени выполнения создать несоответствие между списками брата, сестры и родителя, потому что у вас есть только один список.
Этот вид рефакторинга не волшебная формула, которая работает каждый раз. Вы все еще должны экспериментировать, пока не найдете подходящую для каждого отдельного обстоятельства, но я обнаружил, что это работает примерно в 95% случаев. В оставшиеся времена вам просто нужно жить с неловкостью.
источник
Если бы я реализовывал это, я бы поместил Attach как в A, так и в B. Внутри метода Attach я бы вызвал Attach для переданного объекта. Таким образом, вы можете вызвать либо A.Attach (B), либо B.Attach ( А). Мой синтаксис может быть неправильным, я не использовал c ++ годами:
Альтернативным методом было бы создание 3-го класса, C, который управляет статическим списком пар AB.
источник