Ile to już wody upłynęło od czasu wydania książki z wzorcami projektowymi “Gang of Four” na temat wzorców projektowych. Planuję napisać kilka artykułów na temat programowania reaktywnego, a jakby nie patrzeć wzorzec obserwator ma z tym paradygmatem programowania wiele wspólnego.
1. Obserwator
Wzorzec obserwator jest wzorcem behawioralnym. Jak sama nazwa mówi jest to wzorzec do obserwowania/nasłuchiwania na jakieś zdarzenie (np. zmianę stanu). Jeśli to zdarzenie wystąpi, wszystkie obiekty które “zapisały” się do nasłuchiwania na ten event zostaną o tym fakcie poinformowane. Tworzy się tu relacja jeden-do-wielu, która łączy ze sobą grupę obiektów, które zostaną poinformowane o zmianie stanu.
2. Hierarchia klas
Tak prezentuję się hierarchia klas z konkretną implementacją:
Najważniejsze dwa interfejsy to Observer oraz Subject.
3. Observer
Czyli nasz obserwator. Obiekt, który chce obserwować, posiada w sobie tylko jedną metodę update()
(nie musi być ona tak nazwana jednakże jest to “good practice”). Metoda ta wykonuję logikę, która wywoływana jest w momencie pojawienia się nowego zdarzenie. Zdarzenie to publikowane jest przez Subject (nazywanego też czasem Publisher).
4. Subject
Interfejs, który posiada trzy metody:
registerObserver
– dodaj subskrybenta,unregisterObserver
– usuwa z listy subskrybentów,notifyObservers
– poinformuj wszystkich subskrybentów z listy.
5. Jak korzystać
Jako przykład tworzymy Subject/Publisher’a, który publikuje informację o nowym wpisie na blogu:
public class Blog implements Subject, Publisher { /** Publish message. */ private static final String publishMessage = "Article: %s added"; /** List with all blog subscribers. */ private List<Observer> blogSubscribers; /** * Constructor of {@link Blog}. */ public Blog() { this.blogSubscribers = new ArrayList<>(); } @Override public void registerObserver(Observer observer) { blogSubscribers.add(observer); } @Override public void unregisterObserver(Observer observer) { blogSubscribers.remove(observer); } @Override public void notifyObservers() { blogSubscribers.forEach(Observer::update); } @Override public void publish(String articleName) { System.out.println(String.format(publishMessage, articleName)); notifyObservers(); } }
Teraz utworzymy subskrybentów naszego bloga, którzy chcą otrzymywać informację o nowych wpisach:
public class BlogSubscriber implements Observer { /** Message with information about new article. */ private static final String message = "Hi %s, you have %d new articles to read"; /** User name. */ private final String userName; /** Number of new articles to read. */ private int newArticles; /** * Constructor of {@link BlogSubscriber}. * * @param userName the user name. */ public BlogSubscriber(String userName) { this.userName = userName; } @Override public void update() { newArticles++; System.out.println(String.format(message, userName, newArticles)); } }
Na koniec musimy stworzyć klienta, który będzie korzystał z naszego rozwiązania:
public class Main { public static void main(String[] args) { createBlogChannel(); } private static void createBlogChannel() { Blog blog = new Blog(); BlogSubscriber subscriberAgnieszka = new BlogSubscriber("Agnieszka"); blog.registerObserver(subscriberAgnieszka); blog.publish("Observer"); BlogSubscriber subscriberKrzysztof = new BlogSubscriber("Krzysztof"); blog.registerObserver(subscriberKrzysztof); blog.unregisterObserver(subscriberAgnieszka); blog.publish("Design"); } }
A w wyniku otrzymamy:
Article: Observer added Hi Agnieszka, you have 1 new articles to read Article: Design added Hi Krzysztof, you have 1 new articles to read Article: DesignSecond added Hi Krzysztof, you have 2 new articles to read Article: DesignThird added Hi Krzysztof, you have 3 new articles to read
6. Reaktywny obserwator
Observable obserwuje jakieś zdarzenie i informuje o tym eventem. Observer chce obserwować ten event więc subskrybuje się do Observable.
public interface Observer<T> { void onCompleted(); void onError(Throwable var1); void onNext(T var1); }
- onNext, gdy pojawi się nowy event,
- onError, gdy podczas przetwarzania strumieni wystąpił błąd,
- onCompleted informuje obserwatorów, że skończył się strumień lub przetwarzanie strumienia.
public interface Observable<T> { public Subscription subscribe(Observer<? super T> subscriber); }
Daje możliwość subskrybcji.
Całość znajdziecie na GitHubie.