Stworzyłem dwie abstrakcyjne klasy Subject i Observer, które definiują klasyczny interfejs wzorców Observer. Czerpię z nich implementację wzorca Observer. Obserwator może wyglądać następująco:
void MyClass::Update(Subject *subject)
{
if(subject == myService_)
{
DoSomething();
}
else if(subject == myOtherService_)
{
DoSomethingElse();
}
}
To dobrze i mówi mi, kto coś zmienił. Jednak nie mówi mi, co się zmieniło. Czasami jest to w porządku, ponieważ po prostu zapytam podmiot o najnowsze dane, ale innym razem muszę wiedzieć, co dokładnie zmieniło się w temacie. Zauważyłem w Javie, że mają zarówno metodę replaceObservers (), jak i metodę noticeObservers (Object arg) , aby przypuszczalnie określać szczegóły zmian.
W moim przypadku muszę wiedzieć, czy jedno z kilku różnych działań miało miejsce w tym temacie, a jeśli jest to określone działanie, znać liczbę całkowitą związaną z tym działaniem.
Więc moje pytania to:
- jaki jest sposób C ++, aby przekazać ogólny argument (tak jak Java)?
- Czy Observer jest nawet najlepszym wzorem? Może jakiś system wydarzeń?
AKTUALIZACJA
Znalazłem ten artykuł, który mówi o szablonowaniu wzorca Observer: Implementowanie wzorca Temat / Obserwator za pomocą szablonów . To sprawiło, że zastanawiałem się, czy możesz szablonować argument.
Znalazłem pytanie o przepełnienie stosu, które mówi o szablonowaniu argumentu: Wzorzec obserwatora oparty na szablonie - czy powinienem użyć static_cast lub dynamic_cast . Wydaje się jednak, że PO ma problem, na który nikt nie odpowiedział.
Inną rzeczą, którą mogłem zrobić, to zmienić metodę Update, aby pobierała obiekt EventArg jak w:
void MyClass::Update(Subject *subject, EventArg arg)
{
...
A następnie utwórz podklasy EventArg dla konkretnych danych argumentów, a następnie przypuszczam, że wrzuciłem ją z powrotem do konkretnej podklasy w ramach metody aktualizacji.
AKTUALIZACJA 2
Znalazłem także artykuł na temat tworzenia asynchronicznej struktury c ++ opartej na komunikatach; część 2 omawiającej mające Temat komunikować szczegóły dotyczące tego, co zmieniło.
Teraz poważnie rozważam użycie Boost.Signals . Używanie własnego wzorca obserwatora miało sens, gdy było proste, ale szablonowanie typu i argument zaczyna się komplikować. I może potrzebuję bezpieczeństwa wątku Boost.Signals2.
AKTUALIZACJA 3
Znalazłem też kilka interesujących artykułów na temat wzorca obserwatora:
Generalizing Observer autorstwa Herb Sutter
Implementowanie wzorca obserwatora w C ++ - część 1
Doświadczenia związane z wdrażaniem wzorca projektowego obserwatora (część 2)
Doświadczenia związane z wdrażaniem wzorca projektowego obserwatora (część 3)
Jednak zmieniłem moją implementację na używanie Boost.Signals, które, choć być może nieco rozdęte dla moich celów, z powodzeniem działa. I prawdopodobnie wszelkie obawy o wzdęcie lub prędkość są nieistotne.
źródło
Odpowiedzi:
Czy C ++ lub Java, zgłoszenie do obserwatora może przyjść wraz z informacjami o co się zmieniło. Te same metody replaceObservers (Object arg) mogą być również używane w C ++.
Ogólnie rzecz biorąc, problem pozostanie taki, że może istnieć wiele podmiotów wysyłających do jednego lub wielu obserwatorów, a zatem
class arg
nie mogą być zakodowane na stałe.Zwykle najlepszym sposobem jest utworzenie arg w postaci ogólnej wiadomości / tokenów, które tworzą ten sam typ danych dla różnych klas, ale wartości różnią się dla różnych obserwowanych klas. Alternatywnie, jeśli wszystkie takie wartości powiadomień są wyprowadzane z klasy w pewnej klasie bazowej, która jest wspólna dla wszystkich.
W przypadku wzorca obserwatora ważne jest, aby typ danych Arg nie był zakodowany na stałe między obserwowanym a obserwatorem - w przeciwnym razie jest to sprzężenie, które utrudnia ewolucję.
EDYCJA
Jeśli chcesz, aby obserwator nie tylko obserwował, ale także musiał wykonać wiele innych zadań w oparciu o to, co się wtedy zmieniło , możesz także sprawdzić wzorzec Odwiedzającego . We wzorcu gościa obserwator / odwiedzający wywołuje zmodyfikowany obiekt, dzięki czemu może nie tylko wiedzieć, czym jest modyfikacja, ale może faktycznie nad nią pracować
źródło
Object
(void *
,boost::any
lub coś podobnie ogólne) niż jeśli przejdą szczególnego rodzaju, ponieważ z typem konkretnego zobaczysz w czasie kompilacji, że coś się zmieniło, gdy z typu rodzajowego to skompiluje się i przestanie działać, ponieważ obserwator nie będzie mógł pracować z faktycznie przekazanymi danymi.std::function
Boostboost::function
, Gtk +GClosure
, metody związane z pythonami itp.), Więc po prostu definiuję metody z odpowiednimi podpisami i pytam system o utworzenie rzeczywistych obserwator. Sprzężenie jest rzeczywiście tylko jednym sposobem, podmiot definiuje interfejs dla obserwatorów, ale nie ma pojęcia o ich implementacji.Istnieje kilka sposobów wysłania ogólnego argumentu zdarzenia „Java Like” w C ++.
1) Zadeklaruj argument zdarzenia jako nieważny * i umieść go w odpowiedniej klasie w module obsługi zdarzeń.
2) Zadeklaruj argument zdarzenia jako wskaźnik / odwołanie do nowej klasy / interfejsu, takiego jak (w odpowiedzi na twój przykład)
I niech rzeczywiste klasy argumentów zdarzeń pochodzą z tej klasy.
Jeśli chodzi o twoje pytanie dotyczące obserwatora opartego na szablonie, sprawdź
http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ
źródło
Nie wiem, czy to kanoniczna nazwa, ale z moich dawnych czasów Smalltalk pamiętam termin „aspekt”, aby określić, co zmieniło się na obserwowalnym.
Nie jest to tak skomplikowane, jak pomysł na EventArg (i jego podklasowanie); po prostu przekazuje ciąg znaków (może być nawet liczbą całkowitą) z obserwowalnego do obserwatora.
Plus: istnieją tylko dwie proste metody (
update(observable)
iupdateAspect(observable, aspect)
Minus: obserwator może poprosić obserwowalnego o dodatkowe informacje (tj. „Liczbę całkowitą”)
źródło