Ing. del software B
Il Pattern “Observer”
Simone Magnolini
Scopo
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.
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.
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
La relazione non è unidirezionale, chiunque può intervenire per
cambiare lo stato della presentazione
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
Alunno 1
Presentazione
reagisci()
statoAlunno
getState()
setState()
statoPresentazione
Alunno 2
Alunno N
reagisci()
reagisci()
statoAlunno
statoAlunno
Struttura observer
Ascoltatore
Presentazione
reagisci()
getState()
setState()
statoPresentazione
Alunno 1
Alunno 2
Alunno N
reagisci()
reagisci()
reagisci()
statoAlunno
statoAlunno
statoAlunno
Struttura observer
Lezione
Ascoltatore
aggiungi(Ascoltatore)
reagisci()
rimuovi(Ascoltatore)
notifica()
Presentazione
Alunno 1
Alunno 2
Alunno N
getState()
reagisci()
reagisci()
reagisci()
setState()
statoPresentazione
statoAlunno
statoAlunno
statoAlunno
Struttura observer
Lezione
Ascoltatore
aggiungi(Ascoltatore)
reagisci()
rimuovi(Ascoltatore)
notifica()
Presentazione
Alunno 1
Alunno 2
Alunno N
getState()
reagisci()
reagisci()
reagisci()
setState()
statoPresentazione
statoAlunno
statoAlunno
statoAlunno
Struttura observer
Subject
attach(Observer)
detach(Observer)
Observer
update()
notify()
ConcreteSubject
ConcreteObserver
getState()
update()
setState()
subjectstate
observerState
Il Pattern “Observer”
Anche conosciuto come
Publish-Subscribe
Dependents
Java Delegation Event Model
Intuitivamente correlato a
programmazione “a eventi”
tutti
i
Classificazione
Comportamentale
Focalizzato sulle relazioni run-time tra oggetti
paradigmi
di
Motivazione
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 c’entra per forza una GUI)
Es. il meccanismo dei trigger in una base di dati relazionale
Esempio classico
A
B
C
44
33
D
333
88
A
B
C
D
350
300
250
200
150
100
50
0
A
B
C
D
Applicabilità
Se un’astrazione presenta due aspetti in cui uno è dipendente
dall’altro. 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)
Partecipanti: Subject
“Conosce” tutti gli observer
Chiunque implementi l’interfaccia observer può osservare il soggetto
Offre metodi per:
L’aggiunta 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 un’interfaccia
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() all’interno di setState()
Al termine della modifica di stato il cambiamento viene notificato a tutti gli osservatori, anche a
chi l’ha 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 l’interfaccia Observer
Implementa il metodo update(), quest’ultimo aggiorna lo stato memorizzato per mantenere la
sincronia
Collaborazioni: iscrizione
aConcreteSubject
aConcreteObserver
anotherConcrete
Observer
attach(this)
attach(this)
notify ()
update()
getState()
update()
getState()
Collaborazioni: aggiornamento
aConcreteSubject
aConcreteObserver
anotherConcrete
Observer
setSate()
notify ()
update()
getState()
update()
getState()
Conseguenze
Disaccoppiamento tra le classi

Il Subject conosce solo l’interfaccia Observer (non chi l’osserva)
Per ricevere le notifiche un oggetto qualunque deve solo implementare l’interfaccia 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 l’oggetto come parametro della notifica per
rendere evidente chi è stato modificato
3) Responsabilità
Chi è il responsabile di iniziare l’invio di notifiche?
Il Subject
Le operazioni che modificano lo stato dell’oggetto chiamano notify()
L’Observer 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 l’aggiornamento agli osservatori
Meno efficiente, ma più riusabile
Osservatori interessati
Per migliorare l’efficienza gli Observer potrebbero fornire al momento
dell’iscrizione a cosa sono interessati del Subject
Quando ci sono troppi problemi
ChangeManager è
un’istanza del pattern
Mediator ed essendo unico
nell’applicazione 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”
Scarica

Observer