Menu
Coddy logo textTech

Wzorzec Observer

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 68 z 87.

Wzorzec Obserwator to behawioralny wzorzec projektowy, który ustanawia relację jeden-do-wielu między obiektami. Gdy jeden obiekt (podmiot) zmienia stan, wszystkie jego obiekty zależne (obserwatorzy) są automatycznie powiadamiane i aktualizowane. Możesz o tym myśleć jak o subskrypcji newslettera: subskrybenci otrzymują aktualizacje za każdym razem, gdy publikowane są nowe treści.

Wzorzec obejmuje dwa główne elementy: podmiot, który przechowuje listę obserwatorów i powiadamia ich o zmianach, oraz obserwatorów, którzy definiują metodę aktualizacji reagującą na powiadomienia:

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class NewsPublisher {
    private List<Observer> subscribers = new ArrayList<>();
    
    public void subscribe(Observer observer) {
        subscribers.add(observer);
    }
    
    public void unsubscribe(Observer observer) {
        subscribers.remove(observer);
    }
    
    public void publish(String news) {
        for (Observer subscriber : subscribers) {
            subscriber.update(news);
        }
    }
}

class EmailSubscriber implements Observer {
    private String email;
    
    public EmailSubscriber(String email) {
        this.email = email;
    }
    
    public void update(String message) {
        System.out.println("Email to " + email + ": " + message);
    }
}

Gdy wydawca publikuje wiadomości, wszyscy subskrybenci są automatycznie powiadamiani:

NewsPublisher publisher = new NewsPublisher();
publisher.subscribe(new EmailSubscriber("alice@mail.com"));
publisher.subscribe(new EmailSubscriber("bob@mail.com"));

publisher.publish("Breaking news!");
// Wynik:
// E-mail do alice@mail.com: Breaking news!
// E-mail do bob@mail.com: Breaking news!

Wzorzec Obserwatora promuje luźne powiązania: podmiot nie musi znać konkretnych klas swoich obserwatorów, a jedynie wiedzieć, że implementują interfejs Observer. Dzięki temu można łatwo dodawać nowe typy obserwatorów bez modyfikowania podmiotu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system alertów o cenach akcji z użyciem wzorca Obserwator! Stworzysz system, w którym notowania akcji powiadamiają wielu inwestorów za każdym razem, gdy zmieni się cena akcji, dzięki czemu każdy inwestor może reagować na aktualizacje cen na swój sposób.

Rozmieścisz swój kod w czterech plikach:

  • Observer.java: Zdefiniuj interfejs Observer, który zaimplementują wszyscy inwestorzy. Powinien deklarować jedną metodę update(String stockName, double price), za pomocą której obserwatorzy będą otrzymywać powiadomienia o zmianach cen.
  • StockTicker.java: Utwórz klasę podmiotu, która przechowuje listę obserwatorów i powiadamia ich o zmianach cen. Twoja klasa StockTicker powinna mieć:

    Prywatne pole typu String przechowujące nazwę akcji (ustawiane przez konstruktor).

    Prywatne pole typu double przechowujące bieżącą cenę (początkowo 0).

    Prywatną listę do przechowywania subskrybujących obserwatorów.

    Metodę subscribe(Observer observer), która dodaje obserwatora do listy.

    Metodę unsubscribe(Observer observer), która usuwa obserwatora z listy.

    Metodę setPrice(double price), która aktualizuje cenę, a następnie powiadamia wszystkich obserwatorów, wywołując ich metodę update z nazwą akcji i nową ceną.

  • Investors.java: Utwórz dwa różne typy inwestorów implementujące interfejs Observer:

    DayTrader — ma prywatne pole z nazwą ustawianą przez konstruktor. Po aktualizacji wyświetla [name] received alert: [stockName] is now $[price] - Making quick trade!

    LongTermInvestor — ma prywatne pole z nazwą ustawianą przez konstruktor. Po aktualizacji wyświetla [name] received alert: [stockName] is now $[price] - Holding steady.

    Formatuj cenę z dokładnością do dwóch miejsc po przecinku.

  • Main.java: Połącz elementy systemu obserwatorów! Otrzymasz trzy dane wejściowe: nazwę akcji (String), pierwszą aktualizację ceny (double) i drugą aktualizację ceny (double).

    Utwórz obiekt StockTicker z podaną nazwą akcji. Utwórz obiekt DayTrader o nazwie "Alice" oraz obiekt LongTermInvestor o nazwie "Bob", a następnie zasubskrybuj ich obu w notowaniach.

    Ustaw cenę na pierwszą wartość wejściową (wszyscy obserwatorzy otrzymają powiadomienie). Następnie anuluj subskrypcję Alice. Na koniec ustaw cenę na drugą wartość wejściową (tym razem powiadomienie powinien otrzymać tylko Bob).

Otrzymasz trzy dane wejściowe w podanej kolejności: nazwę akcji (String), pierwszą cenę (double), drugą cenę (double).

To wyzwanie pokazuje możliwości wzorca Obserwator: StockTicker nie wie nic o konkretnych typach inwestorów. Wie tylko, że implementują interfejs Observer. Gdy anulujesz subskrypcję Alice, przestaje ona otrzymywać aktualizacje, a Bob nadal otrzymuje powiadomienia. To luźne powiązanie ułatwia dodawanie nowych typów inwestorów bez zmieniania notowań!

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Wczytaj dane wejściowe
        String stockName = scanner.nextLine();
        double firstPrice = scanner.nextDouble();
        double secondPrice = scanner.nextDouble();
        
        // TODO: Utwórz StockTicker z podaną nazwą akcji
        
        // TODO: Utwórz DayTrader o nazwie "Alice"
        
        // TODO: Utwórz LongTermInvestor o nazwie "Bob"
        
        // TODO: Zasubskrybuj Alice i Bob do tickera
        
        // TODO: Ustaw cenę na firstPrice (obaj obserwatorzy otrzymają powiadomienie)
        
        // TODO: Usuń subskrypcję Alice
        
        // TODO: Ustaw cenę na secondPrice (tylko Bob otrzyma powiadomienie)
        
        scanner.close();
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online