Ten fragment jest dość ciekawy. Proponuję otworzyć kod źródłowy bo bez
niego ciężko będzie śledzić to o czym napiszę. Stan kwadracików w tym
miejscu nas nie interesuje.
Klasa Clock jest konkretnym podmiotem i rozsyła do obiektów
obserwujących(wie o nich jedynie że implementują interface Listener),
powiadomienia o tyknięciach zegara. Tyknięcia występują w równych
odstępach czasu (ustawiane w menu). Wizytator został użyty w celu
ominięcia problemu ze Swing'iem. Swing rozsyła w osobnym wątku
powiadomnienia, proste zastosowanie obserwatora niestety nie jest
możliwe. Mogą występować konfilikty wątku głównego programu z wątkiem
rozsyłania zdarzeń Swing. Można to ominąć na kilka sposobów. Jeden to
synchronizacja, na przeszkodzie stoi brak informacji jak długo będzie
wykonywał się kod obserwatora a co za tym idzie nie mamy dostępu do
listy subskrybentów w tym czasie. Inny sposób polega na kopiowaniu
listy obserwatorów przed wysyłaniem powiadomnień, jednak może być
kosztowny.
W Game of Life rozwiązanie wykorzystuje wizytatora i niemutowalne
obiekty. Interface Publisher.Distributor jest wizytatorem, a metoda
deliverTo(Object subscriber) jest metodą visit() z GoF. (zauważmy że
jest tylko jedna metoda visit).
W Publisher jest jeszcze jedna klasa: Node. Jest to lista
subskrybentów jak również struktura danych dla wizytatora (metoda
accept(Distributor deliveryAgent);). Klasa ta jest niezmienna czyli
nie ma potrzeby żeby dostęp do niej był synchronizowany. Publisher (a
nie Clock) utrzymuje listę subskrybentów, ale nie wie nic o
zdarzeniach dlatego to Clock uczestniczy w obserwatorze a nie
Publisher. Jedyne zadanie tej klasy to dostarczyć powiadomienie
poprzez Distributor'a Publisher.publish(Publisher.Distributor
distributor); W tym kontekscie distributor jest obiektem polecenia
gdyż publisher nie wie nic o działaniu distributora a jedynie wywołuje
jego metodę deliverTo(Object subscriber). Rola Publishera sprowadza
się zatem do zarządzania listą subskrybentów. Obiekty Node definiują
metodę accept(Publisher.Distributor deliveryAgent) { // wizytator
deliveryAgent.deliverTo(subscriber); // subscriber jest prywatnym
polem Node
}
Jakie są korzyści tego rozwiązania ? Sami definiujemy w jaki sposób
powiadomienia o zdarzeniach mają być dostarczane poprzez implementacje
Publisher.Distributor (jest to też wzorzec command) Publisher dzięki
temu może być wykorzystywany w praktycznie dowolnym środowisku bez
konieczności zmian. Interfejs Publisher nie jest w żaden sposób
powiązany z obserwatorem czyli możemy wykorzystać go w dowolnym
kontekście (nie jesteśmy ograniczeni do używania jakiegoś konkretnego
interfejsu obserwatora). Publisher grzecznie przyjmuje od nas obiekt
wizytatora (Publisher.Distributor) następnie iteruje po Liście
subskrybentów i na każdym wywołuje metodę accept(distributor).
Przygotuję diagram sekwencj jak ten kod działa. Na pierwszy rzut oka
wygląda na skomplikowany, ale bardzo elegancko rozwiązuje problem
dostarczania powiadomień :)
pozdrawiam.