Articoli di programmazione

Design patterns in pillole: Observer (e due parole sugli eventi in C#)

Il Design Pattern Observer permette di far comunicare i nostri oggetti, permettendo ad un oggetto detto soggetto di inviare notifiche ad uno o più altri oggetti osservatori.

500px-Observer_w_update.svg.png
Questo pattern è la base per la gestione degli Eventi nei linguaggi più diffusi. Era un pattern che esisteva ben prima dello sviluppo ad oggetti perché la sua utilità è indiscussa e le esigenze a cui assolve sono molto diffuse.

Andiamo a vedere alcune porzioni di codice per capre l'essenza del pattern. Il Subject implementa l'interfaccia IObservable, per cui permette di essere "osservato" da altri oggetti Observer. Subject tiene traccia di tutti gli Observer che si registrano presso di lui, in una collection Observers:

public class Subject : IObservable
{
public IList> Observers { get; set; }
...
}


Gli Observer si registrano presso Subject con il metodo Subscribe; in questo modo finiscono nella lista degli osservatori:

public void Subscribe(IObserver observer)
{
if (!Observers.Contains(observer))
{
Observers.Add(observer);
}
}


Quando il soggetto esegue una operazione che deve essere notificata agli osservatori, questo esegue la chiamata al metodo Notify che si occupa di eseguire per ogni Observer registrato, la chiamata al metodo di notifica, che in questo caso è OnActionExecuted:

public void Notify(string message)
{
foreach (var observer in Observers)
{
observer.OnActionExecuted(new Payload { Message = message });
}
}


Da notare che la gestione degli eventi in molti linguaggi funziona proprio così. Ad esempio in C#, per prima cosa si definisce il delegate Notify, poi il relativo evento ActionExecuted. All'interno della classe Subject viene invocato ActionExecuted convenzionalmente dentro ad un metodo protected virtual che si chiama On[nome evento]:

public delegate void Notify();

public class Subject
{
public event Notify ActionExecuted; // event
public void DoSomething()
{
Console.WriteLine("Ops I did it again...");
OnActionExecuted();
}
protected virtual void OnActionExecuted()
{
ActionExecuted?.Invoke();
}
}

Lato Observer il codice si registra presso l'evento ActionExecuted aggiungendosi alla lista di invocazione del delegate multicast Notify; tradotto significa che è come se si aggiungesse alla lista Observers nel codice ad inizio pagina. Quando scatta ActionExecuted nel Subject, viene eseguito a ruota s_ActionExecuted nell'Observer:

class Observer
{
public void DoSomethingElse()
{
var s = new Subject();
s.ActionExecuted += s_ActionExecuted;
s.DoSomething();
}

// event handler
static void s_ActionExecuted()
{
Console.WriteLine("Subject did it again!");
}
}


#programmazione #designPatterns #csharp