Pattern Observer (eventi)
Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 54 di 70.
Il pattern Observer stabilisce una relazione uno-a-molti in cui un oggetto (il soggetto) notifica più oggetti dipendenti (osservatori) quando il suo stato cambia. In C#, gli eventi offrono un modo integrato per implementare questo pattern con eleganza.
Il modello prevede un editore che genera eventi e degli iscritti che vi rispondono. Ecco un esempio pratico di monitoraggio del prezzo di un'azione:
public class Stock
{
private decimal _price;
public event Action<decimal> PriceChanged;
public decimal Price
{
get => _price;
set
{
_price = value;
PriceChanged?.Invoke(_price); // Notifica tutti gli osservatori
}
}
}
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}");
}
}Gli iscritti associano i propri metodi all'evento usando += e possono scollegarli con -=:
var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();
stock.PriceChanged += alert.OnPriceChanged;
stock.PriceChanged += logger.OnPriceChanged;
stock.Price = 150.50m;
// Output:
// Avviso: il prezzo è ora 150.50
// Registrato: 150.50
stock.PriceChanged -= logger.OnPriceChanged; // Annulla l'iscrizione
stock.Price = 155.00m;
// Output: Avviso: il prezzo è ora 155.00Il pattern Observer favorisce un accoppiamento debole: la classe Stock non deve sapere nulla dei suoi osservatori. Nuovi osservatori possono iscriversi senza modificare il publisher, rendendo il sistema facile da estendere e mantenere.
Sfida
FacileCostruiamo un sistema di monitoraggio della temperatura usando il pattern Observer con gli eventi. Creerai un sensore che notifica più osservatori ogni volta che la temperatura cambia: è perfetto per scenari come i sistemi di climatizzazione, in cui diversi componenti devono reagire agli aggiornamenti della temperatura.
Organizzerai il codice in tre file:
TemperatureSensor.cs: crea una classeTemperatureSensornello spazio dei nomiMonitoring. Questo è il publisher, ovvero il soggetto osservato dagli altri oggetti. Il sensore deve avere:- Un campo privato per memorizzare la temperatura corrente
- Un evento chiamato
TemperatureChangedche usaAction<double>per notificare agli iscritti la nuova temperatura - Una proprietà
Temperatureche genera l'evento ogni volta che la temperatura viene impostata su un nuovo valore
Observers.cs: crea due classi osservatore nello stesso spazio dei nomi, che si iscriveranno alle variazioni di temperatura:DisplayUnit- ha un metodoOnTemperatureChanged(double temp)che stampaDisplay: {temp}CAlarmSystem- ha un metodoOnTemperatureChanged(double temp)che stampaAlarm Check: {temp}C
Program.cs: metti insieme tutti gli elementi creando un sensore e entrambi gli osservatori. Iscrivi entrambi gli osservatori all'evento del sensore, quindi aggiorna la temperatura. Dopodiché, annulla l'iscrizione del sistema di allarme e aggiorna nuovamente la temperatura per mostrare che solo l'unità display riceve la notifica.
Riceverai due input:
- Prima lettura della temperatura (ad es.,
22.5) - Seconda lettura della temperatura (ad es.,
28.0)
Il programma deve:
- Creare il sensore e entrambi gli osservatori
- Iscrivere entrambi gli osservatori all'evento
TemperatureChanged - Impostare la temperatura del sensore sul valore del primo input (entrambi gli osservatori devono ricevere una notifica)
- Annullare l'iscrizione di
AlarmSystemall'evento - Impostare la temperatura del sensore sul valore del secondo input (solo il display deve ricevere una notifica)
Ad esempio, se gli input sono 22.5 e 28.0, l'output deve essere:
Display: 22.5C
Alarm Check: 22.5C
Display: 28CQuesto dimostra il potenziale fondamentale del pattern Observer: al sensore non interessa sapere chi lo sta ascoltando. Gli osservatori possono iscriversi e annullare l'iscrizione dinamicamente, e il publisher si limita a notificare chiunque sia attualmente in ascolto!
Provalo tu
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// Leggi le temperature in ingresso
double temp1 = Convert.ToDouble(Console.ReadLine());
double temp2 = Convert.ToDouble(Console.ReadLine());
// TODO: Crea un'istanza di TemperatureSensor
// TODO: Crea istanze di DisplayUnit e AlarmSystem
// TODO: Iscrivi entrambi gli osservatori all'evento TemperatureChanged
// TODO: Imposta la temperatura del sensore su temp1 (entrambi gli osservatori ricevono una notifica)
// TODO: Annulla l'iscrizione di AlarmSystem all'evento
// TODO: Imposta la temperatura del sensore su temp2 (solo il display riceve una notifica)
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniNamespace e direttiveIntroduzione a classi e oggettiLa parola chiave "this"Metodi e parametriCampi e proprietàCostruttoriInizializzatori di oggettiRiepilogo - Calcolatrice semplice4Ereditarietà
Sintassi di base dell'ereditarietà (:)La parola chiave 'base'Parole chiave virtual e overrideClassi sealedLa classe base 'object'Riepilogo - Gerarchia dei dipendenti7Funzionalità avanzate
Sovraccarico degli operatoriIndicizzatori (this[])Override di ToString()Metodi di estensioneRiepilogo - Lista personalizzata10Pattern di progettazione Parte 1
Introduzione ai pattern di progettazioneSingleton thread-safePattern FactoryPattern Observer (eventi)Pattern Strategy2Proprietà e membri statici
Proprietà implementate automaticamenteProprietà di sola lettura e di sola scritturaCampi e metodi staticiClassi staticheMembri con corpo di espressione5Polimorfismo e interfacce
Polimorfismo a compile time e runtimeInterfaccia o classe astrattaInterfacce multipleInterfacce espliciteUpcasting e downcastingRipasso: calcolatore di forme8Concetti avanzati di programmazione a oggetti
Composizione invece dell'ereditarietàTipi generici (classi e metodi)Delegati ed eventiAttributi e reflectionIDisposable e istruzione usingNozioni di base sull'iniezione delle dipendenze11Pattern di progettazione, parte 2
Pattern del ComandoPattern dell’AdattatorePattern del DecoratorePattern Template MethodPattern dello StatoPattern Composito3Architettura delle classi
Dati di istanza vs staticiParole chiave 'readonly' e 'const'Campi di supportoRiepilogo - Gestore di conti bancari6Incapsulamento
Modificatori di accessoProprietà per l'incapsulamentoImplementazione dell'occultamento dei datiModelli di immutabilitàRiepilogo - Registri degli studentiEsercitati da solo: Compilatore C# online