Il pattern Observer

Notificare i cambiamenti di stato con il pattern Observer

Il pattern Observer viene utilizzato quando oggetti diversi devono conoscere i cambiamenti di stato di un particolare oggetto. Nello specifico, codifica il meccanismo degli eventi e ascoltatori (osservatore). Per poter svolgere questo compito, il pattern Observer propone di utilizzare un metodo notify per avvisare gli ascoltatori. Sarà responsabilità del Soggetto di registrare e notificare gli ascoltatori al verificarsi di un evento.

Si può dire che il pattern Observer interviene quando esiste un oggetto che deve essere costantemente monitorato (detto Subject) da altri oggetti che osservano i suoi cambiamenti di quest’ultimo (detti Observers). Sarà responsabilità del Subject notificare i suoi cambi di stato attraverso dei riferimenti agli oggetti che devono essere avvisati.

Il pattern Observer è di tipo comportamentale e le sue specifiche prevedono:

  1. Contesto:
    • Un oggetto (soggetto) genera eventi;
    • Uno o più oggetti (osservatori) vogliono essere informati del verificarsi di tali eventi.
  2. Soluzione:
    • Definire un’interfaccia con un metodo notify, che sarà implementata dagli osservatori;
    • Il soggetto ha un metodo (attach) per registrare un osservatore;
    • Il soggetto gestisce l’elenco dei suoi osservatori registrati;
    • Quando si verifica un evento, il soggetto informa tutti gli osservatori registrati, chiamando il loro metodo notify.

Il pattern Observer

Nel diagramma UML è presente l’interfaccia Subject (il soggetto osservato) e l’interfaccia Observer (l’osservatore). Subject definisce la classe che deve essere “osservata” e conosce i suoi osservatori possedendo un insieme di riferimenti di tipo Observer; in questo modo, ogni Subject terrà traccia degli osservatori che si sono registrati chiamando attach. ConcreteSubject invoca le operazioni di notifica ereditate dal Subject, quando devono essere informati i ConcreteObserver dei suoi cambiamenti. I ConcreteObserver implementano l’operazione di aggiornamento degli Observer.

JButton, un esempio del pattern Observer in Java

Il pattern Observer

In Java, un esempio pratico d’utilizzo del pattern Observer è l’implementazione di un bottone. La classe JButton che rappresenta un bottone vuole notificare la pressione del bottone a degli oggetti che precedentemente hanno richiesto di essere avvisati. Come si può notare, esiste una corrispondenza diretta con lo schema proprio del pattern Observer. In particolare, JButton ricopre il ruolo di Subject e ActionListener quello di Observer. Il pattern Observer viene supportato dalla libreria standard tramite la classe Observable e l’interfaccia Observer, del package java.util.
L’interfaccia Observer rappresenta un generico osservatore:

  public interface Observer {
    void update(Observable o, Object arg);
  }

Il metodo update corrisponde a quello che nel pattern viene chiamato notify, verrà invocato dall’oggetto osservato al verificarsi degli eventi di interesse. I due argomenti di update sono l’oggetto osservabile che sta chiamando e un argomento libero, che l’applicazione può utilizzare a piacere.
La classe Observable rappresenta un generico oggetto osservabile e i suoi metodi principali sono:

  •   public boolean hasChanged(){ ... }

    Il metodo hasChanged verifica se l’oggetto ha cambiato il suo stato interno;

  •   protected void clearChanged(){ ... }

    Resetta lo stato di this, dichirando che non ci sono stati cambiamenti;

  •   public void addObserver(Observer o){ ... }

    Questo metodo consente di aggiunge un osservatore all’oggetto, corrisponde al metodo attach del pattern;

  •   public void notifyObservers(Object arg){ ... }

    Se ci sono stati cambiamento (hasChanged vero), il metodo notifyObservers invoca il metodo update di tutti gli osservatori registrati e poi cancella i cambiamenti con clearChanged.

Implementare il pattern con l’interfaccia Observable

L’uso tipico di Observable è di estenderla, in questo modo potremo aggiungere alla classe la capacità di essere osservata. Altre classi implementeranno l’interfaccia Observer e fungeranno da osservatori. In tal modo grazie al pattern, non dovremo preoccuparci di implementare il meccanismo di registrazione e notifica degli osservatori. Naturalmente, la classe che estende Observable perde la possibilità di utilizzare l’ereditarietà per altri scopi, dato che in Java è possibile estendere una sola classe.

Un esempio di registrazione di un Observer ad un Subject è il seguente:

  Osservatore obs = new Osservatore();
  Subject subject = new Subject();
  subject.addObserver(obs);
  subject.startProcess();

I vantaggi del pattern Observer consistono nel minimizzazione l’accoppiamento tra Observer e Subject, che possono essere usati indipendentemente gli uni dagli altri. Gli Observer possono essere aggiunti senza modificare il Subject e il Subject conosce solo la lista dei suoi Observer, e non le classi concrete degli Observer, ma soltanto l’interfaccia. Gli Observer possono essere aggiunti a run time e possono regolarsi se reagire o meno ad una notifica del Subject.

Gli svantaggi consistono in una possibile cascata di notifiche. Gli Observer mutuamente si ignorano, un’eventuale richiesta di modifica può avere effetti incontrollati, scatenando la reazione degli altri Observer. Inoltre, essendo molto semplice l’interfaccia di notifica, risulta difficile stabilire il tipo di modifica che avviene nel Subject, di conseguenza risulta difficile a gli Observer di regolarsi a sua volta.

Per la lista completa di tutti i design pattern fate riferimento a questa pagina oppure all’indice della guida avanzata di Java.

Pubblicato in Design Pattern, Guide, Java, Programmazione Taggato con: , , , , , , , ,

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

*