w , ,

FajneFajne Dobre!Dobre! ŚwietnieŚwietnie Że co?Że co? WnerwWnerw SmuteczekSmuteczek

Observer, wzorzec projektowy – Twoje obiekty zawsze na bieżąco

Wzorzec projektowy obserwator

Observer – operacyjny (behawioralny) wzorzec projektowy, który umożliwia automatyczne powiadomienie i aktualizację obiektu (klienta) przez obiekt, który jest nasłuchiwany (tzw. subject).
Przede wszystkim sprawdza się on w przypadkach, kiedy musimy zaktualizować obiekt klienta, ale nie mamy jasnej informacji, kiedy subject zakończy swoją pracę.
W związku z tym ten wzorzec projektowy jest bardzo często wykorzystywany w aplikacjach wielowątkowych oraz systemach rozproszonych.

Wzorzec Observer – założenia aplikacji

Na przykład chcemy zrobić aplikacje, która sprawi, że Czytelnik bloga zawsze zostanie powiadomiony o nowym wpisie. Dlatego jednym z rozwiązań jest ciągłe odpytanie Bloga czy już pojawił się nowy wpis. Jednak odpytywanie będzie musiało odbywać się cyklicznie co bliżej nieokreślony interwał czasowy – co samo w sobie nie jest optymalne.

Znacznie lepszym rozwiązaniem jest zapisanie się na blogu do listy subskrybentów, a następnie zostanie powiadomionym o tym kiedy pojawi się nowy wpis. Dlatego przykład ten stanowi dobre, praktyczne odzwierciedlenie tego jak działa wzorzec projektowy Observer.

Implementacja wzorca Observer

Rozpocznijmy od zaimplementowania interfejsu, który umożliwi nam dokonywanie powiadomień:

public interface MailObserver {
    void newsletter(String mail);
}

Zawiera metoda przyjmuje parametr mail – czyli daną, którą będzie mógł otrzymać użytkownik.

User – klasa nasłuchująca

Klasa użytkownika musi tą informacje odczytywać, więc należy w niej zaimplementować powyższy interfejs.

public class User implements MailObserver {

    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void newsletter(String mail) {
        System.out.println(name + " got email! " + mail);
    }
}

Dla rozróżnienia użytkowników dodałem w modelu imię odbiorcy i przykładowo nadpisałem metodę pochodzącą z interfejsu MailObserver.

Blog – klasa nasłuchiwana

Teraz najważniejszy element – Subject. Innymi słowy Subject, to klasa, która będzie nasłuchiwana – w naszym przypadku będzie to Blog. Przede wszystkim klasa ta musi mieć możliwość przyjmowania obiektów nasłuchujących – typu MailObserver.

Dodatkowo w klasie uwzględniłem wątek, który symuluje działanie wykonywania operacji. Ponadto w wątku losowany jest czas, co który ma zostać wysłany komunikat do użytkownika. Tym komunikatem jest losowy ciąg tekstowy.
Na końcu operacji notyfikowane są wszystkie obiekty nasłuchujące:

public class Blog {

    private List<MailObserver> mailObservers;

    public Blog() {
        mailObservers = new ArrayList<>();
    }

    public void subscribe(MailObserver observers) {
        this.mailObservers.add(observers);
    }

    public void startWork() {
        Thread thread = new Thread(() -> {
            while (Thread.currentThread().isAlive()) {
                int randomDelay = new Random().nextInt(1000);
                try {
                    Thread.sleep(randomDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (MailObserver observer : mailObservers) {
                    observer.newsletter("time: " + randomDelay + ", content: " + UUID.randomUUID().toString());
                }
            }
        });
        thread.start();
    }
}

Inicjalizacja obiektów i uruchomienie przykładu

Samo wykonanie przykładu polega na utworzenie instancji klasy Blog, a następnie przypisanie mu kilku użytkowników, którzy będą go nasłuchiwali.

public class Main {
    public static void main(String[] args) {
        Blog blog = new Blog();
        blog.subscribe(new User("Przemysław"));
        blog.subscribe(new User("Adam"));
        blog.startWork();
    }
}

Na koniec rezultat otrzymania danych z konsoli:

Przemysław got email! time: 357, content: a3dc8676-7c59-4493-9a1f-5cf849a76e22
Adam got email! time: 357, content: 7e150df1-4eff-46be-8d0b-a66b5d408f1d
Przemysław got email! time: 851, content: 907ae678-ba7a-43f0-8104-6572c005a9cb
Adam got email! time: 851, content: d4ce70e1-363f-4a65-8412-b502bddbcc0b
Przemysław got email! time: 537, content: e4c681bc-26a2-4dd6-b1cb-a2cb24f68f5a
Adam got email! time: 537, content: e5d8e84a-bc60-42e4-9107-b135fa15aa85
Przemysław got email! time: 449, content: 60ac3584-e6b5-4d30-ae8f-807e3bbc5b3b
Adam got email! time: 449, content: 17309826-8060-40ae-a488-991c52c6ce86

Moje zastosowania wzorca Observer

Przede wszystkim Observer to mój ulubiony wzorzec. Sprawdził mi się, kiedy tworzyłem aplikacje mobilne i musiałem poczekać na odpowiedź z serwera zdalnego. Jednak do tego czasu nie mogłem wyświetlić użytkownikowi danych na interfejsie użytkownika, co zastąpiłem komunikatem „Proszę czekać…” a w momencie, kiedy komplet danych został wczytany to mój interfejs graficzny dzięki wykorzystaniu tego wzorca otrzymał notyfikacje i w ten sposób wyświetlił co trzeba 😉
Tutaj link do repo: https://github.com/bykowski/design-pattern-observer i proszę o łapkę w górę! 🙂

Przy okazji warto zapoznać się programowaniem reaktywnym, ponieważ to podejście do programowania, które narodziło się z tego wzorca 🙂

Napisane przez Przemysław Bykowski

Aktywny programista i energiczny trener. Specjalizuje się w Spring Boot i uczę go w ramach AkademiaSpring.pl. Po godzinach udzielam się na YouTubach. Więcej o mnie.

Dodaj komentarz

Singleton

Typy generyczne na platformie .NET