Per la pro­gram­ma­zio­ne di software bisogna con­si­de­ra­re diversi aspetti: il prodotto finale non solo deve avere le funzioni de­si­de­ra­te, ma anche un codice sorgente il più leggibile e com­pren­si­bi­le possibile. Il tutto deve avvenire con il minimo sforzo, in par­ti­co­la­re quando i programmi o parti di essi sono pro­get­ta­ti con funzioni o elementi ri­cor­ren­ti. Per questo scopo, i co­sid­det­ti pattern o schemi GoF (“Gang of Four”) offrono una serie di modelli ri­so­lu­ti­vi pre­de­fi­ni­ti per diverse classi di problemi durante la fase di creazione di software.

Oltre ad altri pattern noti, come il Visitor pattern o il Singleton pattern, anche il co­sid­det­to Observer pattern fa parte di questa raccolta di pratici schemi pro­get­tua­li che per­met­to­no di sem­pli­fi­ca­re no­te­vol­men­te la pro­gram­ma­zio­ne di routine. Vi spie­ghe­re­mo cosa comporta l’Observer design pattern (in­clu­den­do una rap­pre­sen­ta­zio­ne grafica in UML) e vi il­lu­stre­re­mo i punti forti e deboli di questo schema.

Cos’è l’Observer pattern?

L’Observer design pattern, ab­bre­via­to in Observer pattern, è uno tra gli schemi più amati per il design di software in­for­ma­ti­ci. Presenta la pos­si­bi­li­tà di definire una di­pen­den­za da uno a molti, quindi tra due o più oggetti, per co­mu­ni­ca­re le modifiche com­ples­si­ve a un oggetto preciso nel modo più semplice e rapido possibile. A tal fine, qualsiasi oggetto, che in questo caso agisce come Observer o os­ser­va­to­re, può re­gi­strar­si con un altro oggetto. Quest’ultimo, che in questo caso si definisce soggetto, informa gli os­ser­va­to­ri re­gi­stra­ti non appena ci sono delle modifiche o degli ag­giu­sta­men­ti.

Come già accennato, anche l’Observer pattern fa parte degli schemi GoF pub­bli­ca­ti nel 1994 nel “Design Patterns: Elements of Reusable Object-Oriented Software”. Si tratta di più di 20 schemi ri­so­lu­ti­vi per il design di software che svolgono ancora oggi un ruolo im­por­tan­te per la con­ce­zio­ne e l’ela­bo­ra­zio­ne di ap­pli­ca­zio­ni in­for­ma­ti­che.

Scopo e fun­zio­na­men­to dell’Observer pattern

L’Observer pattern lavora con due tipi di attori: da una parte troviamo il subject (soggetto), ossia un oggetto il cui stato deve essere osservato a lungo termine. Dall’altra parte troviamo invece gli oggetti che osservano (Observer o os­ser­va­to­ri), che devono essere informati di tutte le modifiche apportate al subject.

Fatto

Di solito a un subject sono at­tri­bui­ti più observer. In linea di massima, però, l’Observer pattern può essere applicato anche in caso di un solo oggetto che osserva.

Senza l’ausilio dell’Observer pattern, gli oggetti che osservano do­vreb­be­ro ri­chie­de­re al subject ag­gior­na­men­ti sullo stato a in­ter­val­li regolari; ciascuna richiesta im­pli­che­reb­be i relativi tempi di calcolo e le risorse hardware ne­ces­sa­rie. L’idea alla base dell’Observer pattern è di cen­tra­liz­za­re il processo di in­for­ma­zio­ne in un soggetto. Questo porta alla creazione di una lista in cui inserire gli os­ser­va­to­ri. In caso di una modifica, il soggetto informa gli os­ser­va­to­ri re­gi­stra­ti nella lista, senza che questi debbano diventare attivi. Se non si desidera più un ag­gior­na­men­to au­to­ma­ti­co dello stato di un de­ter­mi­na­to oggetto che osserva, esso viene sem­pli­ce­men­te rimosso dalla lista.

N.B.

Per informare i singoli os­ser­va­to­ri sono di­spo­ni­bi­li due modelli: nel modello push, il subject comunica lo stato mo­di­fi­ca­to insieme alla notifica. Questo può però com­por­ta­re problemi quando si tra­smet­to­no in­for­ma­zio­ni che l’observer non è in grado di uti­liz­za­re. Questo problema non si verifica nel metodo pull: qui il subject trasmette solo l’in­for­ma­zio­ne di una modifica avvenuta. Gli os­ser­va­to­ri possono ri­chie­de­re infine in­for­ma­zio­ni det­ta­glia­te sullo stato mo­di­fi­ca­to con una richiesta separata.

Rap­pre­sen­ta­zio­ne grafica dell’Observer pattern (diagramma ULM)

Il fun­zio­na­men­to e l’uso di Design Pattern come l’Observer pattern sono spesso in­com­pren­si­bi­li a un occhio esterno. Una rap­pre­sen­ta­zio­ne grafica dello schema pro­get­tua­le può sem­pli­fi­car­ne la com­pren­sio­ne. In par­ti­co­la­re UML (Unified Modeling Language), il lin­guag­gio di mo­del­la­zio­ne am­pia­men­te diffuso, rende visibili e com­pren­si­bi­li le di­pen­den­ze sia per gli utenti tra­di­zio­na­li sia per quelli più esperti. Per questo motivo abbiamo deciso di attingere a UML come lin­guag­gio rap­pre­sen­ta­ti­vo per la seguente rap­pre­sen­ta­zio­ne astratta dell’Observer pattern.

Quali sono i vantaggi e gli svantaggi dell’Observer design pattern?

Uti­liz­za­re l’Observer pattern per lo sviluppo di software può aiutare in molte si­tua­zio­ni. Il maggior vantaggio offerto da questo modello è l’elevato grado di in­di­pen­den­za tra un oggetto osservato (subject) e gli oggetti che osservano e che si orientano allo stato attuale di quest’oggetto. L’oggetto osservato, ad esempio, non deve disporre di alcuna in­for­ma­zio­ne sui suoi os­ser­va­to­ri, perché l’in­te­ra­zio­ne avviene in­di­pen­den­te­men­te dall’in­ter­fac­cia dell’observer. Gli oggetti che osservano ricevono i relativi ag­gior­na­men­ti in modo au­to­ma­ti­co, eli­mi­nan­do del tutto le inutili richieste dal sistema dell’Observer pattern (perché il subject non è cambiato).

Non è però sempre un vantaggio che il soggetto informi in modo au­to­ma­ti­co tutti gli os­ser­va­to­ri re­gi­stra­ti riguardo alle relative modifiche: le in­for­ma­zio­ni relative alle modifiche, infatti, vengono co­mu­ni­ca­te anche se ir­ri­le­van­ti per un observer. Questo può avere un impatto par­ti­co­lar­men­te negativo quando il numero di os­ser­va­to­ri re­gi­stra­ti è molto alto perché a questo punto lo schema dell’observer potrebbe impiegare molte risorse. Un altro problema dell’Observer pattern è che spesso nel codice sorgente del subject non è chiaro quali os­ser­va­to­ri debbano ricevere le in­for­ma­zio­ni.

Observer pattern: dove si utilizza?

L’Observer design pattern è richiesto so­prat­tut­to per quelle ap­pli­ca­zio­ni basate su com­po­nen­ti il cui stato

  • da una parte è osservato as­si­dua­men­te dalle altre com­po­nen­ti,
  • dall’altra è sot­to­po­sto a modifiche regolari.

Tra i tipici casi d’uso troviamo le GUI (Graphical User In­ter­fa­ces), che servono agli utenti per co­mu­ni­ca­re con un software at­tra­ver­so un’in­ter­fac­cia. Non appena i dati vengono mo­di­fi­ca­ti, devono essere ag­gior­na­ti in tutte le com­po­nen­ti della GUI: uno scenario perfetto per la struttura soggetto-os­ser­va­to­re dell’Observer pattern. Anche i programmi che lavorano con record da vi­sua­liz­za­re (tabelle classiche o diagrammi grafici) sfruttano l’ordine offerto dallo schema pro­get­tua­le.

Per quel che riguarda il lin­guag­gio di pro­gram­ma­zio­ne, in linea di massima non ci sono li­mi­ta­zio­ni precise per l’Observer design pattern. È im­por­tan­te soltanto che sia sup­por­ta­to il paradigma orientato all’oggetto per dare senso all’im­ple­men­ta­zio­ne dello schema. Tra i linguaggi che ricorrono spesso a questo pattern troviamo C#, C++, Java, Ja­va­Script, Python e PHP.

Observer pattern: esempio di utilizzo

L’im­ple­men­ta­zio­ne dell’Observer design pattern nei diversi linguaggi di pro­gram­ma­zio­ne può variare a volte in modo si­gni­fi­ca­ti­vo. Il principio alla base resta però sempre lo stesso: un oggetto preciso, ossia il suo stato, viene reso ac­ces­si­bi­le in modo più semplice ad altri oggetti. Un esempio concreto è offerto dal tutorial in tedesco dell’Observer Pattern su ja­va­be­gin­ners.de, che vi il­lu­stre­re­mo e sul quale ci orien­te­re­mo.

Nell’esempio, deve essere mostrato un testo pub­bli­ca­to da un “narratore” nel relativo campo a più “ascol­ta­to­ri”. La classe narratore (il subject) amplierà per questo scopo la classe Ob­ser­va­ble al metodo ad­dOb­ser­ver(). Questo per­met­te­rà di ag­giun­ge­re ascol­ta­to­ri (gli Observer). Inoltre sarà aggiunto il metodo set­Chan­ged(), che registra modifiche al subject e in caso di novità richiama no­ti­fyOb­ser­vers() per informare tutti gli os­ser­va­to­ri.

class narratore extends Observable {
	public narratore(){
		this.addObserver(new ascoltatore_1());
		this.addObserver(new ascoltatore_2());
		tell("Text");
	}
	public void tell(String info){
		if(countObservers()>0){
			setChanged();
			notifyObservers(info);
		}
	}
}

Gli observer ri­chie­do­no inoltre un’im­ple­men­ta­zio­ne dell’in­ter­fac­cia os­ser­va­to­re che include il metodo udpate() e due argomenti: l’oggetto osservato e la modifica in forma di istanza di oggetto (Con­cre­te­Su­b­ject).

class ascoltatore extends JFrame implements Observer{
	private JTextField field;
	public ascoltatore(){
		field1 = new JTextField("a");
		add(field);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(300, 50);
		setVisible(true);
	}
	public void update(Observable o, Object arg) {
		field.setText((String) arg);
	}
}
Vai al menu prin­ci­pa­le