Powiedzmy, że mam dwa typy obiektów, A i B. Relacje między nimi są wiele do wielu, ale żaden z nich nie jest właścicielem drugiego.
Zarówno wystąpienia A, jak i B muszą być świadome połączenia; to nie tylko jeden sposób.
Możemy to zrobić:
class A
{
...
private: std::vector<B *> Bs;
}
class B
{
private: std::vector<A *> As;
}
Moje pytanie brzmi: gdzie mam umieścić funkcje do tworzenia i niszczenia połączeń?
Czy powinien to być A :: Attach (B), który następnie aktualizuje A :: Bs i B :: As wektory?
A może powinien to być B :: Attach (A), co wydaje się równie rozsądne.
Żadne z tych nie wydaje się właściwe. Jeśli przestanę pracować z kodem i wrócę po tygodniu, jestem pewien, że nie będę w stanie przypomnieć sobie, czy powinienem wykonywać A.Attach (B) lub B.Attach (A).
Być może powinna to być taka funkcja:
CreateConnection(A, B);
Ale tworzenie funkcji globalnej również wydaje się niepożądane, biorąc pod uwagę, że jest to funkcja specjalnie do pracy tylko z klasami A i B.
Kolejne pytanie: jeśli często napotykam ten problem / wymaganie, czy mogę w jakiś sposób znaleźć ogólne rozwiązanie tego problemu? Być może klasa TwoWayConnection, z której mogę czerpać lub korzystać z klas dzielących ten rodzaj relacji?
Jakie są dobre sposoby poradzenia sobie z tą sytuacją ... Wiem, jak całkiem dobrze poradzić sobie z sytuacją „C jest właścicielem D” jeden do wielu, ale ta jest trudniejsza.
Edycja: aby wyjaśnić, to pytanie nie wiąże się z problemami własnościowymi. Zarówno A, jak i B są własnością jakiegoś innego obiektu Z, a Z zajmuje się wszystkimi kwestiami własności. Interesuje mnie tylko sposób tworzenia / usuwania połączeń wiele do wielu między A i B.
Pointer
iGestureRecognizer
. Wskaźniki są własnością klasy InputManager i są przez nią zarządzane. GestureRecognizers są własnością instancji Widget, które z kolei są własnością instancji Screen, która jest własnością instancji aplikacji. Wskaźniki są przypisywane do GestureRecognizers, aby mogli przekazywać im surowe dane wejściowe, ale GestureRecognizers muszą być świadomi, ile wskaźników jest obecnie z nimi powiązanych (aby odróżnić gesty 1 palcem od 2 palców itp.).Odpowiedzi:
Jednym ze sposobów jest dodanie do każdej klasy
Attach()
metody publicznej i chronionejAttachWithoutReciprocating()
. ZrobićA
iB
wspólnych znajomych tak, że ichAttach()
metody mogą dzwonić InnegoAttachWithoutReciprocating()
:Jeśli zastosujesz podobne metody
B
, nie będziesz musiał pamiętać, do której klasy zadzwonićAttach()
.Jestem pewien, że możesz podsumować to zachowanie w
MutuallyAttachable
klasie, która zarówno dziedziczy , jakA
iB
dziedziczy, unikając w ten sposób powtarzania się i zdobywania punktów bonusowych w Dzień Sądu. Ale nawet niewyszukane podejście typu „wdrażaj w oba miejsca” wykona zadanie.źródło
MutuallyAttachable
klasa, którą napisałem do tego zadania, jeśli ktoś chce go ponownie użyć: goo.gl/VY9RB (Pastebin) Edycja: Ten kod można ulepszyć, czyniąc go klasą szablonów.MutuallyAttachable<T, U>
szablon zajęć w Gist. gist.github.com/3308058Czy sama relacja może mieć dodatkowe właściwości?
Jeśli tak, to powinna to być osobna klasa.
Jeśli nie, wystarczy dowolny mechanizm zarządzania listami posuwisto-zwrotnymi.
źródło
Zwykle, gdy wpadam w takie sytuacje, które wydają mi się dziwne, ale nie jestem w stanie powiedzieć dlaczego, to dlatego, że próbuję umieścić coś w niewłaściwej klasie. Prawie zawsze istnieje sposób, aby przenieść niektóre funkcje poza klasę
A
iB
wyjaśnić.Jednym z kandydatów do przeniesienia powiązania jest kod, który tworzy powiązanie w pierwszej kolejności. Może zamiast zadzwonić
attach()
, po prostu przechowuje listęstd::pair<A, B>
. Jeśli istnieje wiele miejsc tworzących skojarzenia, można je wydzielić na jedną klasę.Innym kandydatem do przeniesienia jest
Z
w twoim przypadku obiekt, który jest właścicielem powiązanych obiektów .Innym częstym kandydatem do przeniesienia skojarzenia jest kod, który często wywołuje metody
A
lubB
obiekty. Na przykład zamiast wywoływaća->foo()
, które wewnętrznie robi coś ze wszystkimi powiązanymiB
obiektami, zna już skojarzenia i wezwaniaa->foo(b)
dla każdego z nich. Ponownie, jeśli nazywa to wiele miejsc, można je wyodrębnić w jedną klasę.Wiele razy obiekty, które tworzą, posiadają i wykorzystują powiązanie, są tym samym obiektem. To może bardzo ułatwić refaktoryzację.
Ostatnią metodą jest wyprowadzenie relacji z innych relacji. Na przykład bracia i siostry są związkami wielu do wielu, ale zamiast utrzymywać listę sióstr w klasie braci i odwrotnie, wyprowadzasz ten związek ze związku rodzicielskiego. Inną zaletą tej metody jest to, że tworzy ona jedno źródło prawdy. Nie ma możliwości, aby błąd lub błąd w czasie wykonywania utworzył rozbieżność między listami brata, siostry i rodzica, ponieważ masz tylko jedną listę.
Ten rodzaj refaktoryzacji nie jest magiczną formułą, która działa za każdym razem. Nadal musisz eksperymentować, aż znajdziesz odpowiednie dopasowanie do każdej indywidualnej okoliczności, ale okazało się, że działa to w około 95% przypadków. Pozostały czas musisz po prostu żyć z niezręcznością.
źródło
Gdybym to zaimplementował, umieściłbym Attach w A i B. Wewnątrz metody Attach wywołałbym Attach na przekazanym obiekcie. W ten sposób można wywołać A.Attach (B) lub B.Attach ( ZA). Moja składnia może być niepoprawna, od lat nie używałem c ++:
Alternatywną metodą byłoby utworzenie trzeciej klasy, C, która zarządza statyczną listą par AB.
źródło