“No man is an island”, diceva Don Box a proposito di “Indigo” (ora WCF), a PDC 2003. La comunicazione, in qualunque forma la si voglia intendere, è la prima forma di evoluzione. La stessa regola vale anche per le nostre applicazioni modulari, che vivono in un ecosistema comune (la shell), si avvalgono di servizi, e nelle quali sarebbe impossibile fare a meno di una forma di comunicazione condivisa.
Prism mette a disposizione un’infrastruttura di comunicazione che va sotto il nome di Event Aggregator. L’event Aggregator si basa cui concetti di
- Evento: dichiarato in modo che l’aggregator lo possa “vedere”
- Publisher: pubblicatore dell’evento; è l’entità che solleva l’evento
- Subscriber: sottoscrittori dell’evento: sono le entità che si sono registrate per reagire all’evento
Evento
Un evento, in una Prism Application, è una classe che viene creata ereditando dalla classe CompositePresentationEvent che è a sua volta l’implementazione della classe EventBase. La classe CompositePresentationEvent è la classe che mantiene la lista dei sottoscrittori e si prende cura di instradare correttamente gli eventi sollevati dai pubblicatori. La creazione di un evento è una cosa estremamente semplice, come si può vedere nello snippet seguente:
1: public class CustomerSelected : CompositePresentationEvent<string>{}
In pratica, la creazione di un evento è rappresentata da una classe senza implementazione, che eredita da CompositePresentationEvent e dichiara il suo Payload, ovvero il tipo del messaggio che va a trasportare.
A questo punto, non rimane altro che andare a vedere come si può pubblicare (sollevare) un evento e come un’entità si può registrare per mettersi in ascolto di un evento pubblicato.
Publisher
Un publisher, ovvero un modulo che voglia sollevare un evento, deve reperirlo dall’ EventAggregator e richiamare il metodo Publish dell’evento stesso, fornendo ovviamente i parametri che l’evento richiede. Nel nostro caso, l’evento CustomerSelect richiederà come parametro una stringa:
1: EventAggregator.GetEvent<CustomerSelected>().Publish(“CUST001”);
Subscriber
Un subscriber, ovvero un modulo che voglia intercettare un evento, deve sottoscriverlo reperendolo dall’EventAggregator e richiamando il metodo Subscribe con parametro il nome della funzione di callback che verrà utilizzata per gestire l’evento stesso. Tale funzione dovrà avere come parametro una variabile del tipo “trasportato” dal nostro evento. Nel nostro caso si tratterà di una stringa e il codice sarà simile al seguente:
1: public void Run()
2: {
3: ...
4: eventAggregator.GetEvent<CustomerSelected>().Subscribe(SelectCustomer);
5: }
6:
7: public void SelectCustomer(string customerCode)
8: {
9: ...
10: //Read Customer data from DB or other datasource
11: _customerData.GetCustomerDetail(customerCode);
12: ...
13: }
C’è comunque da fare una piccola distinzione funzionale dei due tipi di comunicazione che si vogliono implementare:
- Comunicazione modulo-host: il modulo solleva un evento e l’host viene notificato (ad esempio, per aggiornare una status bar)
- Comunicazione modulo-modulo: un modulo solleva un evento e questo viene intercettato da un altro modulo che fa uso dei dati trasmessi dal publisher
Perchè c’è da fare una distinzione? Perchè è molto importante dove questi eventi vengono creati, per la loro visibilità e condivisione.
Nel caso di comunicazione modulo-host, gli eventi dovranno essere creati e “registrati” dall’host. Essi dovranno essere inoltre “well-known”, ovvero ben documentati ed utilizzabili dai nostri moduli o dai moduli che delle terze parti svilupperanno in futuro. Potrebbe essere sicuramente una buona idea crearli in un assembly differente da quello della shell, in modo da dover referenziare quell’assembly, e non la shell dai nostri moduli.
Nel caso di comunicazione modulo-modulo la questione si fa leggermente più spinosa, in quanto, come si può vedere negli snippet sopra riportati, per sottoscrivere un evento è necessario averlo referenziato nel codice del proprio modulo. Se, come può accadere, questo evento viene dichiarato in un altro modulo, sarà necessario creare una referenza tra i due moduli, cosa che non è sempre desiderabile.
Concludendo, l’infrastruttura di comunicazione basata sull’EventAggregator è estremamente semplice e flessibile, e permette di implementare comunicazioni efficienti tra l’host ed i moduli ed anche intra-modulo. In un successivo post vedremo altre tecniche di comunicazione che ci mette a disposizione l’infrastruttura di Prism.