Ing. del software B Il Pattern Observer Simone Magnolini
Definire una dipendenza uno a molti fra oggetti, in modo tale che se un oggetto cambia il suo stato, tutti gli oggetti dipendenti da questo ricevano una notifica e si aggiornino automaticamente. Scopo
Un esempio concreto Prendiamo ad esempio questa stessa presentazione: L'uno: la presentazione, ha uno stato (il numero della slide ad esempio) che pubblica, per questo in generale si identifica come publisher o subject. La relazione non è unidirezionale, chiunque può intervenire per cambiare lo stato della presentazione I molti: le persone presenti in aula, sono interessate allo stato del soggetto che hanno sottoscritto di osservare (tramite il loro ingresso in aula) per questo si identificano come subscriber o observer
Doveri e responsabilità Presentazione Mantenere uno stato, cambiarlo, restituirlo Inoltre in un qualche modo quando cambia stato deve comunicare la modifica avvenuta, come ad esempio... Pubblico Reagire al cambio di stato nel modo opportuno Modificare lo stato di ciò che sta osservando
Struttura observer Presentazione getState() setState() statoPresentazione Alunno 1 reagisci() statoAlunno Alunno N reagisci() statoAlunno Alunno 2 reagisci() statoAlunno
Struttura observer Presentazione getState() setState() statoPresentazione Alunno 1 reagisci() statoAlunno Alunno N reagisci() statoAlunno Alunno 2 reagisci() statoAlunno Ascoltatore reagisci()
Struttura observer Presentazione getState() setState() statoPresentazione Alunno 1 reagisci() statoAlunno Alunno N reagisci() statoAlunno Alunno 2 reagisci() statoAlunno Ascoltatore reagisci() Lezione aggiungi(Ascoltatore) rimuovi(Ascoltatore) notifica()
Struttura observer Presentazione getState() setState() statoPresentazione Alunno 1 reagisci() statoAlunno Alunno N reagisci() statoAlunno Alunno 2 reagisci() statoAlunno Ascoltatore reagisci() Lezione aggiungi(Ascoltatore) rimuovi(Ascoltatore) notifica()
Struttura observer ConcreteSubject getState() setState() subjectstate ConcreteObserver update() observerState Observer update() Subject attach(Observer) detach(Observer) notify()
Anche conosciuto come Publish-Subscribe Dependents Java Delegation Event Model Intuitivamente correlato a tutti i paradigmi di programmazione a eventi Classificazione Comportamentale Focalizzato sulle relazioni run-time tra oggetti Il Pattern Observer
Viste multiple dello stato pubblicato Più di un formato di visualizzazione Devono essere coerenti Viste multiple interattive, che permettono la modifica dello stato Occorre aggiornare anche le altre viste Devono essere coerenti Paradigma a eventi (non centra per forza una GUI) Es. il meccanismo dei trigger in una base di dati relazionale Motivazione
Esempio classico
Se unastrazione presenta due aspetti in cui uno è dipendente dallaltro. Separandoli è possibile riutilizzarli Se cambiamenti ad un oggetto hanno ripercussioni su altri oggetti il cui numero è variabile, o comunque non è noto a priori Se un oggetto deve inviare messaggi ad altri oggetti senza sapere esattamente di che tipo sono legami di dipendenza deboli (disaccoppiamento) Applicabilità
Partecipanti: Subject Conosce tutti gli observer Chiunque implementi linterfaccia observer può osservare il soggetto Offre metodi per: Laggiunta di un osservatore (attach) La rimozione di un osservatore (detach) In java potrebbe essere implementata come abstract Senza istanze, ma con dei metodi implementati
Partecipanti: Observer Fornisce uninterfaccia Tutti gli osservatori devono ereditarla per ottenere gli aggiornamenti del soggetto In java potrebbe essere implementata come interface Senza istanze né metodi implementati
Partecipanti: ConcreteSubject Memorizza lo stato che interessa ai ConcreteObserver Lo ritorna, in generale, con il metodo getState() nel caso Java NOTA: Bisogna chiamare notify() allinterno di setState() Al termine della modifica di stato il cambiamento viene notificato a tutti gli osservatori, anche a chi lha prodotto
Partecipanti: ConcreteObserver Mantiene un riferimento ad un oggetto ConcreteSubject di interesse In realtà un singolo osservatore può guardare più soggetti Memorizza lo stato Quello che dovrebbe essere sincronizzato/aggiornato Implementa linterfaccia Observer Implementa il metodo update(), questultimo aggiorna lo stato memorizzato per mantenere la sincronia
Collaborazioni: iscrizione aConcreteSubject aConcreteObserver attach(this) notify () update() anotherConcrete Observer getState()
Collaborazioni: aggiornamento aConcreteSubject aConcreteObserver setSate() notify () update() anotherConcrete Observer getState()
Conseguenze Disaccoppiamento tra le classi Il Subject conosce solo linterfaccia Observer (non chi losserva) Per ricevere le notifiche un oggetto qualunque deve solo implementare linterfaccia Observer Broadcast I messaggi possono essere notificati a tutti gli Observer chiamando notify() Si possono rimuovere e aggiungere Observer a piacere Il problema degli update non attesi Gli Observer non si conoscono Non possono sapere i reali effetti di operazioni compiute sul Subject Possono esserci effetti a cascata di aggiornamenti e sincronizzazioni,anche incompleti
Problemi implementativi
1) Tracciare le dipendenze Mantenere le associazioni Il Subject tiene traccia di tutti i propri Observer Necessario per sapere a chi mandare le notifiche Efficiente nel caso ideale (pochi Subject, molti Observer) Il sovraccarico delle strutture dati può diventare significativo se la situazione è invertita (pochi Observer e molti Subject) Strutture associative per mappare Subject e Observer (es. Hash Table) Risparmio dello spazio, ma penalità nel tempo
2) Tanti Subject Tante cose da osservare Un Observer può essere interessato alle notifiche di più Subject Un oggetto che necessità di più strutture dati per funzionare Es. Un grafico che rappresenta le relazioni tra due entità In questo caso potrebbe essere utile passare loggetto come parametro della notifica per rendere evidente chi è stato modificato
3) Responsabilità Chi è il responsabile di iniziare linvio di notifiche? Il Subject Le operazioni che modificano lo stato delloggetto chiamano notify() LObserver può ignorare il problema Più operazioni di questo tipo causano molti update consecutivi Possibile inefficienza Sicuri problemi con il multithreading! Gli Observer Chiamano notify() quando hanno finito di modificare lo stato Più efficiente, più facile gestire il multithreading Più responsabilità per gli Observer, che a questo punto diventano dei client non più passivi del Subject Meno sicuro
4) Distruttori Distruzione del Subject In generale, la soluzione migliore è notificare la situazione agli Observer Non è detto che gli Observer debbano essere distrutti per forza Se osservano più soggetti? Modifica opportuna al distruttore della classe Subject E IN JAVA?????
Altri problemi Auto-consistenza del Subject Prima del notify() tutti gli aggiornamenti devono essere completi Non utilizzare protocolli specifici di aggiornamento Modello push, il Subject inoltra informazioni sulla modifica Più efficiente, ma meno riusabile Modello pull, il Subject delega laggiornamento agli osservatori Meno efficiente, ma più riusabile Osservatori interessati Per migliorare lefficienza gli Observer potrebbero fornire al momento delliscrizione a cosa sono interessati del Subject
Quando ci sono troppi problemi ChangeManager è unistanza del pattern Mediator ed essendo unico nellapplicazione potrebbe essere un Singleton
Implementazione ESEMPIO: Un count down java import java.util.Observable; import java.util.Observer; public class Esempio{ public static void main(String[] args){ // istanzio l'oggetto osservatore e l'oggetto da osservare Osservatore osservatore = new Osservatore(); Osservato osservato = new Osservato(); // aggiungo all'oggetto da osservare l'osservatore osservato.addObserver(osservatore); // faccio partire il conto alla rovescia osservato.contoAllaRovescia(10); }
Implementazione class Osservato extends Observable { public void contoAllaRovescia(int n) { for ( ; n >= 0; n--) { // l'oggetto e' cambiato setChanged(); // notifico il cambiamento all'osservatore notifyObservers(new Integer(n)); }
Implementazione class Osservatore implements Observer { public void update(Observable oggettoOsservato, Object obj) { // ottengo il valore di n passato da notifyObservers ad update int n = ((Integer)obj).intValue(); System.out.println("" + n); }
Ultime note In Java esistono le interfacce Observer e Observable Sono deprecated dalla versione 1.1 Compatibilità retroattiva Casi molto semplici, usi in cui il meccanismo a eventi è probabilmente eccessivo Java Delegation Event Model Si definiscono listeners, event handlers