L'AWT ed i visitor
La modalità di programmazione utilizzata per implementare l'interprete lisp è identica all'AWT 1.02. Per esempio la funzione applyPrimitive è costruita esattamente come la HandleEvent: gestiamo l'evento (nel nostro caso trattiamo la primitiva) e restituiamo true; altrimenti restituiamo false se non era di nostra competenza.
Nel nostro interprete lisp si evidenziano gli stessi problemi che hanno portato la Sun ha modificare la vecchia gestione degli eventi. Se le primitive sono molte aumenta il numero degli if e dunque il codice si complica, oltre a diventare sempre meno efficiente. Lo stesso accade con il metodo HandleEvent che è ottimo per interfacce grafiche semplici, ma praticamente inutilizzabile quando si costruisce qualcosa di più complesso.
Di seguito un estratto dal libro Java 1.1 dell'Apogeo.
Perché esistono due modelli? Quali erano i difetti
del vecchio modello 1.02 che hanno comportato la necessità di una
modifica tanto drastica? Il vecchio modello di eventi non ha nulla di male
in sé: è semplice e facile da apprendere, e funziona bene
per piccole applicazioni Java come gli applet che utilizzano pochi elementi
dell'interfaccia utente ed elaborano soltanto pochi eventi. Tuttavia, quando
si creano applicazioni più complesse, con centinaia di eventi da
elaborare in vari modi, le limitazioni del vecchio modello si fanno evidenti.
Inoltre, il vecchio modello rende difficoltoso lo sviluppo di un sistema
basato su componenti come quello offerto da JavaBeans. Il nuovo modello
1.1 pone rimedio a varie limitazioni del modello 1.02. La prima è
il problema di handleEvent(); come si è spiegato in precedenza,
questo metodo è utilizzato per elaborare tutti gli eventi che potrebbero
essere utili per l'applet, e questo non è il modo migliore di elaborare
gli eventi. Il modello 1.1 supera la limitazione inviando gli eventi all'applet
soltanto se quest'ultimo ha espresso un interesse al riguardo. In questo
modo, la gestione degli eventi risulta meno pesante.
La seconda limitazione del modello di eventi 1.02
è data dal modo in cui sono rappresentati gli eventi. Nel modello
1.02, tutti gli eventi sono istanze della classe Event. Un identificatore
determina il tipo di evento. Per decidere che cosa fare nell'evento, occorre
utilizzare un'enorme istruzione switch all'interno di handleEvent(), in
modo da stabilire di quale tipo di evento si tratti, e quindi elaborarlo
direttamente o trasferirne il controllo a un metodo come mouseDown(). Che
cosa accade se vi sono vari eventi con lo stesso identificatore, ma generati
da diversi elementi dell'interfaccia utente (ad esempio diversi pulsanti
dell'applet)? Occorre aggiungere istruzioni switch o if…else. In questo
modo il programma aumenta di dimensioni e il codice di gestione degli eventi
si fa più complesso, perciò aumenta la probabilità
di introdurre errori...
La modalità di programmazione utilizzata per implementare l'interprete prolog assomiglia invece all'AWT 1.1. L'evento viene gestito direttamente tramite il metodo opportuno, così come da noi una primitiva è trattata dalla visit corrispondente.
Nel nostro interprete sono stati introdotti diversi visitor astratti per evitare ai visitor concreti di dover implementare le visit di tutti gli oggetti, anche quelle non necessarie. Nell'AWT 1.1 questo compito è svolto dagli ascoltatori: ogni ascoltatore contiene i metodi relativi ad una certo numero di eventi, ma non a tutti i possibili eventi esistenti come richiederebbe l'implementazione standard del visitor.
Il nostro interprete si differenzia dall'AWT 1.1 quando si parla di registrazione degli eventi. Infatti nell'estensione dell'interprete viene registrata direttamente la classe tramite il metodo setPrimitive del parser e non l'oggetto come succede nell'AWT. Naturalmente nell'AWT è necessario registrare l'oggetto perché due istanze della stessa classe possono rispondere differentemente allo stesso evento: si pensi ad esempio a due istanze della classe Button. Inoltre registrazione nell'interprete è in realtà legata al parser e non ai visitor. La registrazione per i visitor è nascosta dalla accept e dal double dispatching: quando invochiamo il metodo accept di un oggetto gli passiamo direttamente il visitor che deve gestire l'evento.
Sempre dal libro dell'Apogeo:
La principale differenza tra i due modelli di eventi è che nel nuovo modello l'elaborazione degli eventi è separata in due parti: la prima è l'oggetto che riceve un evento - che può essere l'applet o una sua parte, come un pulsante, la seconda e più importante è l'ascoltatore di eventi. Quest'ultimo rappresenta una determinata serie di eventi (infatti è possibile avere un ascoltatore del mouse, uno della tastiera, uno dello scorrimento e così via) e ha la responsabilità di eseguire un'unione in risposta a tali eventi specifici. Quando si crea un ascoltatore, si inserisce in esso il codice dell'evento. Il ricevitore e l'ascoltatore dell'evento sono uniti tramite la registrazione dell'ascoltatore. Per registrare un ascoltatore con il proprio programma, si utilizza un metodo speciale che in sostanza dice: "questo ascoltatore elaborerà questi eventi". Nel vecchio modello, l'applet (il ricevitore dell'evento) riceve tutti gli eventi generati dal sistema, mentre nel modello nuovo l'applet riceve soltanto gli eventi che hanno un ascoltatore registrato. In questo modo, l'elaborazione è più efficiente sia per il sistema complessivo sia per l'applet, poiché non è necessario controllare sempre tutti gli eventi.