Observer-Muster
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 68 von 87.
Das Observer Pattern ist ein Verhaltensentwurfsmuster, das eine Eins-zu-viele-Beziehung zwischen Objekten herstellt. Wenn ein Objekt (das Subjekt) seinen Zustand ändert, werden alle seine abhängigen Objekte (die Beobachter) automatisch benachrichtigt und aktualisiert. Stell es dir wie ein Newsletter-Abonnement vor: Abonnenten erhalten Updates, sobald neue Inhalte veröffentlicht werden.
Das Muster umfasst zwei Hauptkomponenten: ein Subjekt, das eine Liste von Beobachtern verwaltet und sie über Änderungen benachrichtigt, sowie Beobachter, die eine Aktualisierungsmethode 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 Herausgeber Nachrichten 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 wissen, dass sie die Schnittstelle Observer implementieren. Dadurch lassen sich neue Beobachtertypen einfach hinzufügen, ohne das Subjekt zu ändern.
Aufgabe
EinfachErstellen wir ein Aktienkurs-Benachrichtigungssystem mithilfe des Beobachter-Musters! Du erstellst ein System, in dem ein Aktienticker mehrere Investoren benachrichtigt, sobald sich der Aktienkurs ändert, sodass jeder Investor auf seine eigene Weise auf Kursaktualisierungen reagieren kann.
Du organisierst deinen Code auf vier Dateien verteilt:
Observer.java: Definiere dasObserver-Interface, das alle Investoren implementieren werden. Es sollte eine einzelne Methodeupdate(String stockName, double price)deklarieren, die Beobachter verwenden, um Benachrichtigungen über Kursänderungen zu erhalten.StockTicker.java: Erstelle die Subjektklasse, die eine Liste von Beobachtern verwaltet und sie über Kursänderungen benachrichtigt. DeinStockTickersollte Folgendes enthalten:Ein privates String-Feld für den Namen der Aktie (wird über den Konstruktor festgelegt).
Ein privates double-Feld für den aktuellen Kurs (anfangs 0).
Eine private Liste zum Speichern abonnierter Beobachter.
Eine
subscribe(Observer observer)-Methode, die einen Beobachter zur Liste hinzufügt.Eine
unsubscribe(Observer observer)-Methode, die einen Beobachter aus der Liste entfernt.Eine
setPrice(double price)-Methode, die den Kurs aktualisiert und anschließend alle Beobachter benachrichtigt, indem sie derenupdate-Methode mit dem Namen der Aktie und dem neuen Kurs aufruft.Investors.java: Erstelle zwei verschiedene Arten von Investoren, die dasObserver-Interface implementieren:DayTrader– besitzt ein privates Namensfeld, das über den Konstruktor festgelegt wird. Bei einer Aktualisierung gibt es[name] received alert: [stockName] is now $[price] - Making quick trade!aus.LongTermInvestor– besitzt ein privates Namensfeld, das über den Konstruktor festgelegt wird. Bei einer Aktualisierung gibt es[name] received alert: [stockName] is now $[price] - Holding steady.aus.Formatiere den Kurs mit zwei Dezimalstellen.
Main.java: Führe dein Beobachtersystem zusammen! Du erhältst drei Eingaben: den Namen der Aktie (String), die erste Kursaktualisierung (double) und die zweite Kursaktualisierung (double).Erstelle einen
StockTickermit dem angegebenen Aktiennamen. Erstelle einenDayTradermit dem Namen"Alice"und einenLongTermInvestormit dem Namen"Bob", und abonniere beide beim Ticker.Setze den Kurs auf den ersten Eingabewert (alle Beobachter werden benachrichtigt). Entferne dann Alice. Setze schließlich den Kurs auf den zweiten Eingabewert (diesmal sollte nur Bob benachrichtigt werden).
Du erhältst drei Eingaben in dieser Reihenfolge: Aktienname (String), erster Kurs (double), zweiter Kurs (double).
Diese Herausforderung veranschaulicht die Leistungsfähigkeit des Beobachter-Musters: Der StockTicker weiß nichts über die konkreten Investorentypen. Er weiß lediglich, dass sie Observer implementieren. Wenn du Alice abbestellst, 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!
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 gegebenen Aktiennamen
// TODO: Erstelle einen DayTrader namens "Alice"
// TODO: Erstelle einen LongTermInvestor namens "Bob"
// TODO: Abonniere sowohl Alice als auch Bob beim Ticker
// 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 Schlüsselwort thisMethodenFelder (Attribute)KonstruktormethodeÜberladen von KonstruktorenRückblick – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbung (extends)Das Schlüsselwort superÜberschreiben von Methoden (@Override)KonstruktorverkettungDie Object-KlasseEinfach- und mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenRückblick – Mitarbeiterhierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-SchnittstelleRückblick – Benutzerdefinierte Sortierung2Zugriffsmodifikatoren & Kapselung
Übersicht der ZugriffsebenenGetter- und Setter-MethodenInformationsverbergungDas Schlüsselwort finalRückblick – Bankkontoverwaltung5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, verschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Versiegelte Klassen (Java 17+)11Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBuilder-MusterObserver-MusterStrategie-Muster14Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungsdienst3Klassen-Props & statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Wiederholung – Counter & Utility6Schnittstellen & abstrakte Klassen
Einführung in InterfacesInterfaces implementierenMehrere Interfaces implementierenDefault & Static in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesRückblick – ZahlungssystemÜbe selbstständig: Online-Java-Compiler