Ein Newsletter soll neue Artikel veröffentlichen.
Mehrere Abonnenten möchten benachrichtigt werden, sobald ein neuer Artikel erscheint.
Naive Lösung:
Newsletterkennt direkt jeden konkreten Abonnenten undpublic class Newsletter {
private EmailAbonnent emailAbonnent = new EmailAbonnent();
private AppAbonnent appAbonnent = new AppAbonnent();
public void neuerArtikel(String titel) {
emailAbonnent.benachrichtigen(titel);
appAbonnent.benachrichtigen(titel);
}
}
Problem damit:
Newsletterfest mit jedem konkreten Abonnenten-Typ verdrahtetNewsletter selbstNewsletter (das Subject) und die Abonnenten (die Observer) sind eng gekoppeltNewsletter verwaltet stattdessen nur eine Liste einer gemeinsamen Abonnent-Schnittstelleupdate()), ohne die konkrete Klasse dahinter zu kennenLosgelöst von der Newsletter-Geschichte bleibt als abstrakte Schablone:
interface Observer { void update(ZustandsTyp daten); }
class Subject {
private List<Observer> observers = new ArrayList<>();
void subscribe(Observer o) { observers.add(o); }
void unsubscribe(Observer o) { observers.remove(o); }
void notifyObservers(ZustandsTyp daten) {
for (Observer o : observers) o.update(daten);
}
}
Alles andere (Newsletter/Abonnent, Wetterstation/Anzeige, Aktienkurs/Depot …) ist nur austauschbares Vokabular über dieser Struktur.
Abonnent (Observer-Interface)
update(String titel), die jeder konkrete Abonnent implementieren mussNewsletter (Subject)
List<Abonnent>,anmelden()/abmelden()neuerArtikel() reihum update() bei allen angemeldeten Abonnenten aufEmailAbonnent, AppAbonnent (konkrete Observer)
Abonnent und reagieren jeweils auf ihre eigene Weise auf die BenachrichtigungMain)
Newsletter und die AbonnentenneuerArtikel() die Benachrichtigung ausnew für die Abonnenten außerhalb von Newsletter
Abonnent-Schnittstelleimport java.util.ArrayList;
import java.util.List;
interface Abonnent {
void update(String titel);
}
class Newsletter {
private List<Abonnent> abonnenten = new ArrayList<>();
public void anmelden(Abonnent abonnent) {
abonnenten.add(abonnent);
}
public void abmelden(Abonnent abonnent) {
abonnenten.remove(abonnent);
}
public void neuerArtikel(String titel) {
for (Abonnent abonnent : abonnenten) {
abonnent.update(titel);
}
}
}
class EmailAbonnent implements Abonnent {
private String name;
public EmailAbonnent(String name) {
this.name = name;
}
public void update(String titel) {
System.out.println("[E-Mail an " + name + "] Neuer Artikel: " + titel);
}
}
class AppAbonnent implements Abonnent {
private String name;
public AppAbonnent(String name) {
this.name = name;
}
public void update(String titel) {
System.out.println("[Push-Benachrichtigung an " + name + "] Neuer Artikel: " + titel);
}
}
public class Main {
public static void main(String[] args) {
Newsletter newsletter = new Newsletter();
Abonnent anna = new EmailAbonnent("Anna");
Abonnent ben = new AppAbonnent("Ben");
newsletter.anmelden(anna);
newsletter.anmelden(ben);
newsletter.neuerArtikel("Observer Pattern erklärt");
newsletter.abmelden(ben);
newsletter.neuerArtikel("Zweiter Artikel – Ben ist jetzt abgemeldet");
}
}
Ausgabe:
[E-Mail an Anna] Neuer Artikel: Observer Pattern erklärt
[Push-Benachrichtigung an Ben] Neuer Artikel: Observer Pattern erklärt
[E-Mail an Anna] Neuer Artikel: Zweiter Artikel – Ben ist jetzt abgemeldet
Nach dem abmelden() erhält Ben keine Benachrichtigung mehr – der Newsletter kennt nur noch Anna.
Vorteile:
Newsletter zu ändern (offen für Erweiterung, geschlossen für Modifikation).Nachteile:
abmelden() vergessen, bleiben Referenzen bestehen (Speicherleck / „Lapsed Listener"-Problem).