Observer-Muster (Events)
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 54 von 70.
Das Observer-Muster etabliert eine Eins-zu-viele-Beziehung, bei der ein Objekt (das Subjekt) mehrere abhängige Objekte (Beobachter) benachrichtigt, wenn sich sein Zustand ändert. In C# bieten Events eine integrierte Möglichkeit, dieses Muster elegant zu implementieren.
Das Muster umfasst einen Publisher, der Ereignisse auslöst, und Subscriber, die auf diese reagieren. Hier ist ein praktisches Beispiel für einen Aktienkurs-Monitor:
public class Stock
{
private decimal _price;
public event Action<decimal> PriceChanged;
public decimal Price
{
get => _price;
set
{
_price = value;
PriceChanged?.Invoke(_price); // Alle Beobachter benachrichtigen
}
}
}
public class PriceAlert
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Alert: Price is now {newPrice}");
}
}
public class PriceLogger
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Logged: {newPrice}");
}
}Abonnenten verknüpfen ihre Methoden über += mit dem Ereignis und können diese mit -= wieder trennen:
var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();
stock.PriceChanged += alert.OnPriceChanged;
stock.PriceChanged += logger.OnPriceChanged;
stock.Price = 150.50m;
// Ausgabe:
// Alarm: Preis ist jetzt 150.50
// Protokolliert: 150.50
stock.PriceChanged -= logger.OnPriceChanged; // Abmelden
stock.Price = 155.00m;
// Ausgabe: Alarm: Preis ist jetzt 155.00Das Observer-Muster fördert eine lose Kopplung – die Stock-Klasse muss nichts über ihre Observer wissen. Neue Observer können sich anmelden, ohne den Publisher zu modifizieren, was das System einfach erweiterbar und wartbar macht.
Aufgabe
EinfachErstellen wir ein Temperaturüberwachungssystem mithilfe des Observer-Musters mit Ereignissen. Du erstellst einen Sensor, der mehrere Beobachter benachrichtigt, sobald sich die Temperatur ändert – perfekt für Szenarien wie Klimaregelungssysteme, bei denen verschiedene Komponenten auf Temperaturaktualisierungen reagieren müssen.
Du organisierst deinen Code auf drei Dateien:
TemperatureSensor.cs: Erstelle eineTemperatureSensor-Klasse im NamespaceMonitoring. Dies ist dein Herausgeber – das Subjekt, das von anderen Objekten beobachtet wird. Dein Sensor sollte Folgendes besitzen:- ein privates Feld zum Speichern der aktuellen Temperatur
- ein Ereignis namens
TemperatureChanged, dasAction<double>verwendet, um Abonnenten über die neue Temperatur zu benachrichtigen - eine
Temperature-Eigenschaft, die das Ereignis auslöst, sobald die Temperatur auf einen neuen Wert gesetzt wird
Observers.cs: Erstelle zwei Beobachterklassen im selben Namespace, die sich für Temperaturänderungen registrieren:DisplayUnit– verfügt über eine MethodeOnTemperatureChanged(double temp), dieDisplay: {temp}CausgibtAlarmSystem– verfügt über eine MethodeOnTemperatureChanged(double temp), dieAlarm Check: {temp}Causgibt
Program.cs: Führe alles zusammen, indem du einen Sensor und beide Beobachter erstellst. Registriere beide Beobachter für das Ereignis des Sensors und aktualisiere anschließend die Temperatur. Danach meldest du das Alarmsystem ab und aktualisierst die Temperatur erneut, um zu zeigen, dass nur die Anzeigeeinheit die Benachrichtigung erhält.
Du erhältst zwei Eingaben:
- Erster Temperaturmesswert (z. B.
22.5) - Zweiter Temperaturmesswert (z. B.
28.0)
Dein Programm sollte:
- den Sensor und beide Beobachter erstellen
- beide Beobachter für das Ereignis
TemperatureChangedregistrieren - die Temperatur des Sensors auf den ersten Eingabewert setzen (beide Beobachter sollten benachrichtigt werden)
AlarmSystemvom Ereignis abmelden- die Temperatur des Sensors auf den zweiten Eingabewert setzen (nur die Anzeige sollte benachrichtigt werden)
Wenn die Eingaben beispielsweise 22.5 und 28.0 sind, sollte die Ausgabe folgendermaßen aussehen:
Display: 22.5C
Alarm Check: 22.5C
Display: 28CDies veranschaulicht die zentrale Stärke des Observer-Musters – der Sensor weiß nicht, wer ihm zuhört, und es ist ihm auch egal. Beobachter können sich dynamisch registrieren und abmelden, und der Herausgeber benachrichtigt einfach alle, die aktuell zuhören!
Probier es selbst
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Eingabetemperaturen lesen
double temp1 = Convert.ToDouble(Console.ReadLine());
double temp2 = Convert.ToDouble(Console.ReadLine());
// TODO: Eine TemperatureSensor-Instanz erstellen
// TODO: DisplayUnit- und AlarmSystem-Instanzen erstellen
// TODO: Beide Observer für das TemperatureChanged-Event registrieren
// TODO: Die Temperatur des Sensors auf temp1 setzen (beide Observer werden benachrichtigt)
// TODO: Das AlarmSystem vom Event abmelden
// TODO: Die Temperatur des Sensors auf temp2 setzen (nur das Display wird benachrichtigt)
}
}
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 DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler