Observer-Pattern
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 68 von 87.
Das Observer-Muster ist ein Verhaltensmuster, das eine Eins-zu-viele-Beziehung zwischen Objekten herstellt. Wenn ein Objekt (das Subjekt) seinen Zustand ändert, werden alle seine Abhängigen (die Beobachter) automatisch benachrichtigt und aktualisiert. Stellen Sie es sich wie ein Newsletter-Abonnement vor – Abonnenten erhalten Updates, wann immer neue Inhalte veröffentlicht werden.
Das Muster umfasst zwei Hauptkomponenten: ein Subjekt, das eine Liste von Beobachtern führt und diese über Änderungen benachrichtigt, sowie Beobachter, die eine Update-Methode definieren, um auf Benachrichtigungen zu reagieren:
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);
}
}Wenn der Publisher Neuigkeiten veröffentlicht, werden alle Abonnenten automatisch benachrichtigt:
NewsPublisher publisher = new NewsPublisher();
publisher.subscribe(new EmailSubscriber("alice@mail.com"));
publisher.subscribe(new EmailSubscriber("bob@mail.com"));
publisher.publish("Breaking news!");
// Ausgabe:
// E-Mail an alice@mail.com: Breaking news!
// E-Mail an bob@mail.com: Breaking news!Das Observer-Muster fördert eine lose Kopplung – das Subjekt muss die konkreten Klassen seiner Beobachter nicht kennen, sondern nur, dass sie das Observer-Interface implementieren. Dies macht es einfach, neue Beobachtertypen hinzuzufügen, ohne das Subjekt zu ändern.
Aufgabe
EinfachLassen Sie uns ein Aktienkurs-Alarmsystem mit dem Observer Pattern bauen! Sie werden ein System erstellen, bei dem ein Stock Ticker mehrere Investoren benachrichtigt, wann immer sich der Aktienkurs ändert, sodass jeder Investor auf seine eigene Weise auf Preisaktualisierungen reagieren kann.
Sie werden Ihren Code über vier Dateien hinweg organisieren:
Observer.java: Definieren Sie dasObserver-Interface, das alle Investoren implementieren werden. Es sollte eine einzige Methodeupdate(String stockName, double price)deklarieren, die Observer verwenden, um Benachrichtigungen über Preisänderungen zu erhalten.StockTicker.java: Erstellen Sie die Subject-Klasse, die eine Liste von Observern verwaltet und diese über Preisänderungen benachrichtigt. IhrStockTickersollte Folgendes haben:Ein privates String-Feld für den Aktiennamen (gesetzt über den Konstruktor).
Ein privates Double-Feld für den aktuellen Preis (anfänglich 0).
Eine private Liste zum Speichern der abonnierten Observer.
Eine
subscribe(Observer observer)-Methode, die einen Observer zur Liste hinzufügt.Eine
unsubscribe(Observer observer)-Methode, die einen Observer aus der Liste entfernt.Eine
setPrice(double price)-Methode, die den Preis aktualisiert und dann alle Observer benachrichtigt, indem sie derenupdate-Methode mit dem Aktiennamen und dem neuen Preis aufruft.Investors.java: Erstellen Sie zwei verschiedene Arten von Investoren, die dasObserver-Interface implementieren:DayTrader- hat ein privates Namensfeld, das über den Konstruktor gesetzt wird. Wenn er aktualisiert wird, gibt er[name] received alert: [stockName] is now $[price] - Making quick trade!aus.LongTermInvestor- hat ein privates Namensfeld, das über den Konstruktor gesetzt wird. Wenn er aktualisiert wird, gibt er[name] received alert: [stockName] is now $[price] - Holding steady.aus.Formatieren Sie den Preis mit zwei Dezimalstellen.
Main.java: Bringen Sie Ihr Observer-System zusammen! Sie erhalten drei Eingaben: den Aktiennamen (String), die erste Preisaktualisierung (double) und die zweite Preisaktualisierung (double).Erstellen Sie einen
StockTickermit dem angegebenen Aktiennamen. Erstellen Sie einenDayTradernamens"Alice"und einenLongTermInvestornamens"Bob", und abonnieren Sie beide beim Ticker.Setzen Sie den Preis auf den ersten Eingabewert (alle Observer werden benachrichtigt). Melden Sie dann Alice ab. Setzen Sie schließlich den Preis auf den zweiten Eingabewert (diesmal sollte nur Bob benachrichtigt werden).
Sie erhalten drei Eingaben in der Reihenfolge: Aktienname (String), erster Preis (double), zweiter Preis (double).
Diese Herausforderung demonstriert die Stärke des Observer Patterns: Der StockTicker weiß nichts über die spezifischen Investorentypen – er weiß nur, dass sie Observer implementieren. Wenn Sie Alice abmelden, erhält sie keine Aktualisierungen mehr, während Bob weiterhin benachrichtigt wird. Diese lose Kopplung macht es einfach, neue Investorentypen hinzuzufügen, ohne den Ticker zu ändern!
Spickzettel
Das Observer-Muster (Beobachter-Muster) ist ein Verhaltensmuster, das eine Eins-zu-viele-Beziehung zwischen Objekten herstellt. Wenn ein Objekt (das Subjekt) seinen Zustand ändert, werden alle seine Abhängigen (die Beobachter) automatisch benachrichtigt und aktualisiert.
Das Muster umfasst zwei Hauptkomponenten:
- Subjekt: Verwaltet eine Liste von Beobachtern und benachrichtigt diese über Änderungen
- Beobachter: Definieren eine Update-Methode, um auf Benachrichtigungen zu reagieren
Grundstruktur:
import java.util.ArrayList;
import java.util.List;
interface Observer {
void update(String message);
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer observer) {
observers.add(observer);
}
public void unsubscribe(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
class ConcreteObserver implements Observer {
public void update(String message) {
// Auf Benachrichtigung reagieren
}
}Anwendungsbeispiel:
Subject subject = new Subject();
subject.subscribe(new ConcreteObserver());
subject.notifyObservers("State changed!");
// Alle angemeldeten Beobachter erhalten das UpdateDas Observer-Muster fördert eine lose Kopplung – das Subjekt muss die konkreten Klassen seiner Beobachter nicht kennen, sondern nur, dass sie das Observer-Interface implementieren. Dies macht es einfach, neue Beobachtertypen hinzuzufügen, ohne das Subjekt zu ändern.
Probier es selbst
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Eingaben lesen
String stockName = scanner.nextLine();
double firstPrice = scanner.nextDouble();
double secondPrice = scanner.nextDouble();
// TODO: Erstelle einen StockTicker mit dem angegebenen stockName
// TODO: Erstelle einen DayTrader namens "Alice"
// TODO: Erstelle einen LongTermInvestor namens "Bob"
// TODO: Melde sowohl Alice als auch Bob beim Ticker an
// TODO: Setze den Preis auf firstPrice (beide Beobachter werden benachrichtigt)
// TODO: Melde Alice ab
// TODO: Setze den Preis auf secondPrice (nur Bob wird benachrichtigt)
scanner.close();
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem