Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Observer pattern in PHP

Come monitorare lo stato di un oggetto e notificare oggetti differenti del cambiamento delle sue proprietà. Un esempio di programmazione avanzata
Come monitorare lo stato di un oggetto e notificare oggetti differenti del cambiamento delle sue proprietà. Un esempio di programmazione avanzata
Link copiato negli appunti

L'observer pattern è una particolare tipologia di design pattern, cioè uno schema progettuale o modello in grado di fornire soluzioni per la risoluzione di problematiche che si presentano frequentemente; nell'ambito del paradigma Object-oriented, i design pattern svolgono la loro funzione mettendo a disposizione una rappresentazione delle relazioni e delle dinamiche d'interazione fra classi o oggetti.

Nel caso specifico, l'observer pattern viene adottato per monitorare lo stato di oggetti differenti; in pratica, dati uno o più oggetti, essi verranno registrati in modo da gestire un evento che potrebbe essere prodotto da un ulteriore oggetto, cioè quello posto sotto osservazione, come per esempio un suo cambiamento di stato.

Utilizzabile anche nello sviluppo in PHP, tale schema può aver delle interessanti applicazioni pratiche delle quali verrà fornito un approfondimento nel corso di questa trattazione.

Struttura di un observer pattern

Tecnicamente, un observer pattern permette di estendere o di modificare il comportamento di un classe senza la necessità che essa subisca dei cambiamenti; nello specifico, quando un metodo riceve una chiamata attraverso un oggetto, quest'ultimo segnala l'evento ad altri oggetti che sono in attesa di una notifica riguardante il suo stato.

L'oggetto coinvolto dall'evento prende il nome di subject, mentre quelli destinati a riceverne la notifica sono denominati observers o listeners, la comunicazione tra il primo e i secondi è possibile grazie ad un terzo componente chiamato dispatcher, anche quest'ultimo è definibile come un oggetto e funge essenzialmente come gestore di connessioni.

In tale meccanismo interviene un quarto elemento che funge da agente per l'evento che coinvolge il subject, esso potrebbe essere per esempio un utente che esegue una determinata azione o un semplice processo.

Per via delle peculiarità esposte, nell'economia degli schemi di progettazione gli observer pattern appartengono alla categoria dei cosiddetti pattern comportamentali (behavioral patterns) che riguardano il modo in cui le classi interagiscono con gli oggetti e gli oggetti entrano in relazione tra di loro all'interno di una dinamica di callback (richiamo), in essa uno o più blocchi di codice vengono appunto richiamati da un altro.

In pratica, un observer pattern definisce le dipendenze tra oggetti differenti in modo tale che, quando un oggetto vede il suo stato modificato, le relative dipendenze vengano automaticamente aggiornate.

Implementazione standard degli observer pattern in PHP

In PHP 5.x è disponibile una modalità d'implementazione per gli observer pattern attraverso le librerie SPL (Standard PHP Library), una raccolta di interfacce per l'accesso ai dati e di classi per la risoluzione di problematiche ricorrenti.

Nello specifico entrano in gioco due interfacce che potranno essere utilizzate attraverso delle classi destinate ad implementarle realizzandone i metodi: splSubject e splObserver; alla prima fanno riferimento tre metodi:

  1. SplSubject::attach
  2. SplSubject::detach
  3. SplSubject::notify

splObserver è invece correlato ad un solo metodo, denominato SplObserver::update, la cui funzione è quella di ricevere un aggiornamento dal subject, esso verrà chiamato ogni volta che si verificherà un evento atteso.

Lo schema funzionale implementato dalle SPL per tale pattern è semplice e lineare:

  • l'agente determina un'azione a carico del subject determinandone una modifica di stato;
  • il dispatcher effettua una connessione agli observers;
  • gli observers intercettano la comunicazione transitata attraverso il dispatcher e registrano l'evento riguardante il subject.
  • Un esempio di "traduzione in codice" dello schema presentato potrebbe essere quello in cui si crea un'applicazione che simula un caso concreto, cioè un potenziale debitore (il subject) che effettua una richiesta a due possibili creditori (gli observers) e riceverà risposte differenti a seconda dell'entità di quest'ultima.

    Il primo passaggio sarà quello relativo alla definizione degli observers:

    /**
    la classe Observer implementa l’interfaccia SplObserver
    e introduce i due metodi che verranno invocati al momento
    della registrazione di una modifica di stato del subject
    */
    class Observer implements SplObserver {
       	protected $papero;
       	public function __construct($papero) {
           	$this->papero = $papero;
       	}
    # il nome della classe viene concatenato al costruttore
       	public function __toString() {
           	return "(".__CLASS__.") ".$this->papero;
       	}
    # il metodo update() introduce i metodi richiamati
    # in seguito all’evento in cui è coinvolto il subject
       	public function update(SplSubject $subject) {
           	if ($subject->menoDiMille() == true) {
               	echo $this . ": - Te li presto. <br />";
           	}
           	if ($subject->piuDiMille() == true) {
               	echo $this . ": - Ma in due rate. <br />";
           	}
       	}
    	}
    # il secondo observer viene definito all’interno di una
    # sottoclasse che estende la classe del primo observer
    	class ObserverB extends Observer {
       	public function update(SplSubject $sub) {
           	if ($sub->piuDiMille() == true) {
               	echo $this . ": - Non ti do un centesimo. <br />";
           	}
       	}
    	}

    La classe Observer splObserver

    Essa integra poi il metodo magico __toString()

    Viene quindi richiamato il metodo update()

    La derivata ObserverB update()

    La classe successiva implementa invece l'interfaccia SplObserver SplSubject prossima pagina

    Questa classe implementa invece l'interfaccia SplObserver SplSubject

    /*
    la classe Subject implementa l’interfaccia SplSubject
    */
    class Subject implements SplSubject {
       	private $papero;
       	private $observers = array();
       	private $menodimille = false;
       	private $piudimille = false;
       	public function __construct($papero) {
           	$this->papero = $papero;
       	}
       	public function __toString() {
           	return "(".__CLASS__.") ".$this->papero;
       	}
    # il metodo attach() attribuisce gli observers al subject
       	public function attach(SplObserver $obs) {
           	$this->observers[] = $obs;
       	}
    # il metodo detach() dissocia gli observers al subject
       	public function detach(SplObserver $obs) {
           	$this->observers = array_diff($this->observers, array($obs));
       	}
    # notify() segnala il verificarsi di un evento a carico del subject
       	public function notify() {
           	foreach($this->observers as $observer) {
               	$observer->update($this);
           	}
       	}
    # definizione dei valori di ritorno
       	public function chiedePiuDiMille($val) {
           	$this->piudimille = $val;
           	if ($val == true) {
               	echo $this . ": - Mi servono 1.200 Dollari. <br />";
           	}
          	$this->notify();
       	}
       	public function piuDiMille() {
           	return $this->piudimille;
       	}
       	public function chiedeMenoDiMille($val) {
           	$this->menodimille = $val;
           	if ($val == true) {
               	echo $this . ": - Mi servono 500 Dollari. <br />";
           	}
           	$this->notify();
       	}
       	public function menoDiMille() {
           	return $this->menodimille;
       	}
    	}

    Come è possibile notare dal codice proposto, il metodo attach() update() detach() array_diff()

    notify()

    Il passaggio finale

    /*
    Istanza delle classi e generazione degli oggetti
    associati a subject ed observers
    */
    $observer = new Observer("Paperoga");
    	$observerB = new ObserverB("Paperone");
    	$subject = new Subject("Paperino");
    # invocazione dei metodi
    	$subject->attach($observer);
    	$subject->attach($observerB);
    	$subject->chiedeMenoDiMille(false);
    	$subject->chiedeMenoDiMille(true);
    	$subject->chiedePiuDiMille(true);
    	$subject->detach($observerB);
    	$subject->chiedePiuDiMille(true);

    L'esecuzione dell'applicazione porta alla generazione del seguente output:

    (Subject) Paperino: - Mi servono 500 Dollari.
    (Observer) Paperoga: - Te li presto.
    (Subject) Paperino: - Mi servono 1.200 Dollari.
    (Observer) Paperoga: - Te li presto.
    (Observer) Paperoga: - Ma in due rate.
    (Observer) Paperone: - Non ti do un centesimo.
    (Subject) Paperino: - Mi servono 1.200 Dollari.
    (Observer) Paperoga: - Te li presto.
    (Observer) Paperoga: - Ma in due rate.

    L'istanza delle tre classi disponibili porta all'attribuzione di un nome al subject e ad entrambi gli observers, segue quindi una doppia invocazione del metodo attach()

    Fatto questo, il subject subisce un primo cambiamento di stato, dovuto alla chiamata del metodo chiedeMenoDiMille() chiedePiuDiMille()

    Infine, viene nuovamente invocato il metodo richiamato in precedenza, ma dato che il secondo observer è stato liberato tramite un detach()

    Conclusioni

    L'observer pattern è un particolare tipo di design pattern basato su uno o più oggetti che vengono definiti con lo scopo di effettuare la gestione di un potenziale evento generato da un oggetto posto sotto monitoraggio.

    I suoi ambiti d'utilizzo sono numerosi, in questa trattazione è stato proposto il codice di un'applicazione in grado di fornire risposte differenti alle modifiche di stato di un oggetto, ma gli observer pattern potranno essere utilizzati anche per altri scopi, come per esempio notificare gli effetti di un tentativo di autenticazione in uno script per il login o per definire output differenti a seconda delle eccezioni registrate durante l'esecuzione di un'applicazione, tutto dipende dalle esigenze delle sviluppatore.

Ti consigliamo anche