Dr. Carlo Pescio
Design: Interfaccia Utente

Pubblicato su Computer Programming No. 45


In questa puntata analizzeremo gli elementi fondamentali del dialogo uomo-macchina, discuteremo i modelli di interfaccia e di interazione, ed il progetto del componente di interfaccia.

Introduzione
In questa puntata analizzeremo alcuni degli elementi fondamentali del quarto ed ultimo componente del design, ovvero l'interfaccia con gli utenti del sistema. Si tratta in realtà di un grande settore, che meriterebbe una lunga serie di articoli, ed al quale sono stati dedicati interi volumi; in generale, infatti, lo studio dell'interfaccia utente ricade sotto il più ampio ombrello detto Human-Computer-Interaction, che a sua volta non è che una specializzazione della disciplina del design: progettare un'efficace interfaccia utente per un programma non è molto diverso dal progettare un cruscotto di automobile, i controlli di un apparato stereo o addirittura un rubinetto o una maniglia. In tutti i casi, esistono considerazioni di immediatezza, usabilità, idoneità, non-ambiguità, chiarezza, e così via, che vanno attentamente vagliate se si desidera ottenere il miglior risultato.
Proprio per questa ragione, sarebbe utile per ogni progettista di interfacce, o aspirante tale, estendere la propria conoscenza dell'interazione uomo-oggetti, anche al di fuori del settore informatico; un testo fondamentale, ricco di esempi reali, è [1]; naturalmente, è utile poter far riferimento anche a testi specializzati sull'interazione uomo-computer, come [2], [3] e [4].

Modelli dell'interfaccia utente
Il progetto dell'interfaccia utente dovrebbe sempre essere ottenuto come mediazione tra due modelli, spesso contrapposti: il modello concettuale dell'utente ed il modello del programmatore; lo schema risultante viene definito modello del progettista. Vediamo ora nel dettaglio il significato e le caratteristiche dei diversi modelli.
Il modello concettuale dell'utente è un modello mentale, molto informale, formato da un insieme di relazioni (di cui l'utente assume l'esistenza) tra un insieme di elementi. Queste relazioni sono basate sia sull'esperienza quotidiana dell'utente, sia sulla familiarità con il sistema e la somiglianza con altre applicazioni: in effetti, gli esseri umani creano dei modelli mentali per le nuove situazioni in gran parte basandosi sulla somiglianza con situazioni pregresse. Ad esempio, l'icona di un fax verrà associata all'idea di un programma o di un device per l'invio di fax, in funzione del background dell'osservatore (che ad esempio, potrebbe non avere mai visto un fax). È molto importante cercare di avvicinarsi il più possibile allo schema mentale dell'utente, perché ogniqualvolta si richiede l'adattamento dell'utilizzatore ad un nuovo modello mentale, si rischia di vedere la propria applicazione etichettata come "troppo complessa". Sfortunatamente, gli utenti non sono normalmente in grado di descrivere il loro modello mentale: in effetti, di norma non siamo neppure consci di averlo; esistono molte strategie per "scoprire" tale modello, tutte o quasi basate sui seguenti punti:

Notiamo che l'importanza degli standard è ben spiegata dall'uso di esperienze pregresse per costruire un nuovo modello mentale: se la nuova applicazione condivide elementi standard con altri programmi già noti all'utente, l'impatto sarà certamente più positivo.
Il modello del programmatore è normalmente ben codificato ed esplicito; fanno parte del modello del programmatore le restrizioni tipiche della piattaforma target, le convenzioni di sviluppo e le linee guida del produttore, gli elementi fondamentali di human-computer interaction, e così via. Il programmatore riesce spesso a mappare un modello object oriented nella sua interfaccia in modo quasi canonico, per quanto non necessariamente ottimale. Un record di un database viene immediatamente "immaginato" come una form a livello di interfaccia utente, con tanto di bottoni per la navigazione del database. Purtroppo, la visione del programmatore non di rado si rivela troppo tecnica per l'utente finale, ed è proprio in questo senso che nasce l'esigenza di un modello che, in qualche modo, cerchi di mediare tra le esigenze, talvolta contrastanti, della visione dell'utente e del programmatore.
Il modello del progettista rappresenta proprio questa mediazione; i suoi elementi principali sono: Avendo a disposizione sia i documenti di design, sia una descrizione delle attività degli utenti (come visto in precedenza), lo scopo del progettista è proprio quello di scegliere le azioni e gli oggetti più rappresentativi (non tutti gli utenti avranno infatti lo stesso modello), capire cosa va inserito nel progetto e cosa va escluso per le ragioni più varie (difficoltà eccessiva, tempi di sviluppo, esistenza di prodotti analoghi, eccetera) ed infine quali nuovi elementi debbano essere introdotti nel modello dell'utente (non necessariamente l'automazione ricalca pedissequamente le procedure manuali).
Attraverso varie tecniche, discusse in seguito, il progettista deve far corrispondere ad oggetti del mondo reale alcuni oggetti virtuali, ed esporre dei meccanismi per permettere l'interazione, il più possibile semplice ed "indolore", tra l'utente e tali oggetti virtuali. Il momento fondamentale, prima della scelta delle rappresentazioni visive, è quello delle tecniche e dei meccanismi di interazione: questi verranno analizzati nel prossimo paragrafo.

Modelli di interazione
Esistono due modelli fondamentali per l'interazione uomo-macchina, che non di rado vengono utilizzati all'interno della stessa applicazione in momenti diversi. Il primo modello, chiamato normalmente action-object, è il più indicato per gli utenti inesperti od occasionali, anche se inerentemente meno flessibile. Il secondo, detto object-action, lascia molta libertà operativa all'utente, ma proprio per questa ragione è più indicato per gli esperti, che non necessitano di una impostazione rigida ma semplice da seguire.
Il metodo action-object, come suggerisce il nome, consiste nello scegliere l'azione da compiere, e poi l'oggetto sul quale compierla; esistono molti esempi di applicazione del modello, ad esempio l'apertura di un file all'interno di una applicazione: si seleziona prima l'azione (File-Open) e poi l'oggetto (il file da aprire). Notiamo che in questo caso il sistema ci può guidare, mostrando ad esempio i soli file corrispondenti all'applicazione in uso, posizionandosi in una directory predefinita per quel tipo di file, e così via. In generale, il metodo action-object guida il più possibile l'utente, che in seguito alla selezione di una azione si trova di fronte una serie di scelte, quasi tutte obbligate come la selezione di un oggetto o la compilazione di una form, scelte che eseguite in sequenza portano al completamento di una attività. Naturalmente, in questo caso il progettista costringe l'utente ad operare secondo la propria visione dell'attività, ovvero il proprio percorso logico per portarla a compimento; per un utente occasionale del sistema, o per utenti inesperti, i benefici di una interazione guidata passo-passo superano spesso il fastidio di trovarsi costretti in una sequenza preordinata. Ciò è tanto più vero quanto meno arbitraria è la scelta dei singoli passi di interazione.
Il metodo object-action, al contrario, consiste nello scegliere l'oggetto ed in seguito l'azione da compiere; esempi tipici sono i browser/explorer o i programmi di disegno vettoriale. Nel primo caso, possiamo navigare a piacere il disco, scegliendo in ogni momento l'azione da applicare agli oggetti selezionati: aprire, cancellare, spostare, eccetera. Nel secondo, possiamo selezionare un qualunque oggetto grafico ed applicare qualche azione (ruotare, ingrandire, cambiare il colore, eccetera). L'utente è totalmente libero da costrizioni: nei limiti delle funzionalità previste dal sistema, non vi sono imposizioni di sorta sulla sequenza delle operazioni da utilizzare per il completamento di una attività; l'utente esperto può adattare lo strumento alla propria forma mentale ed alle proprie abitudini di lavoro, anziché essere influenzato e guidato dallo strumento stesso. Viceversa, l'utente un pò sprovveduto si troverà di fronte così tante scelte da esserne intimidito e bloccato: non a caso, in Windows 95 esiste ora il famoso bottone "Start", che ha fatto sorridere molti utenti con un minimo di disinvoltura, ma che ha fornito a tutti i nuovi utenti un "aggancio" di partenza (action-object) verso un'interfaccia che viceversa, nel momento iniziale, era proprio del tipo object-action.
Come scegliere il giusto modello? La scelta non può prescindere dal tipo di applicazione e dall'abilità dell'utente; difficilmente saremo in grado di fornire entrambe le possibilità, e dobbiamo quindi tenere presente anche la rapidità di apprendimento dell'utente sin dall'inizio.
In genere, se gli utenti seguono da tempo una procedura codificata, è semplice replicare la stessa procedura in una struttura action-object; in questo caso, difficilmente l'utente dimostrerà in seguito insofferenza nei confronti del sistema. Lo stesso dicasi di opzioni poco usate del programma, con le quali anche gli utenti esperti saranno meno familiari: essere guidati passo passo in una attività che si svolge una volta l'anno è in genere ritenuto preferibile rispetto alla massima flessibilità.
Per contro, categorie di utenti notoriamente "svegli", rapidi nell'apprendere, e con propensioni personali alla soluzione dei problemi (in questa classe possiamo spesso inserire personale tecnico, ma anche chi svolge attività creative ed artistiche) si troveranno probabilmente più soddisfatti con uno strumento che consenta di svolgere lo stesso compito in molti modi diversi, senza sequenze preordinate.
Va osservato che l'approccio più complesso è quello che sembra meglio adattarsi al paradigma object oriented: si seleziona un oggetto, e poi una delle azioni che possiamo richiedergli di svolgere. L'approccio action-object, come avremo occasione di vedere più avanti, presenta invece un'interfaccia basata sull'astrazione funzionale. Proprio la maggiore complessità di un'interfaccia basata sul modello object-action, tuttavia, dovrebbe ridimensionare le aspettative di chi punta troppo sulle OOUI: l'apprendimento richiede spesso tempi più lunghi, e non sempre il risultato è all'altezza delle premesse. Lo stesso problema, in parte, è anche condiviso dall'approccio document-centric: in molti casi, assumere che l'utente sappia selezionare l'oggetto giusto al momento giusto non riflette le reali capacità ed i reali desideri dell'utente stesso.
Esistono rimedi nel caso una applicazione basata sul modello object-action si riveli troppo complessa per l'utilizzatore? Probabilmente, una soluzione è un training migliore. Un'altra soluzione, che ho avuto modo di sperimentare personalmente, consiste nel sovraimporre una interfaccia action-object ad una object-action. In una applicazione piuttosto complessa una parte di configurazione/design è stata inizialmente sviluppata usando il modello object-action, proprio per la flessibilità che era richiesta. Tuttavia, essendo questa opzione usata di rado, gli utenti continuavano a riportare una eccessiva difficoltà di utilizzo; naturalmente, i più esperti ne erano invece molto soddisfatti. La soluzione adottata è stata di sovraimporre un modello addizionale action-object, molto simile ai wizard che consentono di progettare le form in Access; notiamo che, anche nel caso di Access, i wizard sovraimpongono un approccio action-object, dove l'utente è guidato passo-passo, ad un processo inerentemente object-action (il design della form o del report), lasciando poi all'utente la possibilità di ritornare al modello object-action per una personalizzazione più spinta.

Progettare l'interfaccia utente
La progettazione dell'interfaccia utente non può prescindere dall'ambiente target, che ormai è sempre vincolato da un insieme di linee guida per uniformare il più possibile le applicazioni. In questa sede non vedremo quindi che i principi più generali, applicabili nelle diverse situazioni.
Come abbiamo visto, il modello del programmatore è spesso molto diverso dal modello utente, e scopo del modello introdotto dal progettista è di mediare tra i due. Una tecnica spesso utilizzata, che ha dimostrato la sua efficacia in numerosi progetti, è quella di disaccoppiare i dati e l'interfaccia che agisce su di essi; questo porta direttamente al paradigma Model-View-Controller o Document/View, anche se interessanti generalizzazioni sono state proposte in [5] e [6]. È necessario notare che questo disaccoppiamento, per quanto possa risultare utile in fase di manutenzione e di estensione, causa normalmente un aumento del lavoro di codifica: ciò non deve stupire, in quanto svincolare i dati dalla loro rappresentazione implica una moltiplicazione delle classi. In effetti, molti dei prodotti RAD eliminano proprio questa fase, ponendo forti legami tra l'interfaccia utente ed i dati manipolati, proprio al fine di limare il più possibile i tempi di sviluppo: sta a voi decidere quale approccio sia il più indicato per la vostre applicazioni. Indipendentemente dal metodo utilizzato, va sempre ricordato che non vi deve necessariamente essere una corrispondenza biunivoca tra le classi dei dati e gli oggetti esposti a livello utente; talvolta, l'approccio O.O. rischia di influenzare in modo eccessivo la visione dell'interfaccia utente: lo sviluppatore tende allora a esporre troppo gli oggetti, assumendo che l'utente trovi naturale lavorare in termini di classi e polimorfismo. Viceversa, in molti casi i concetti sottostanti di polimorfismo e (soprattutto) di ereditarietà vanno abilmente nascosti, o meglio presentati sotto aspetti più immediatamente fruibili dall'utente: come vedremo più avanti, in molti casi un raggruppamento di tipo funzionale è preferibile ad uno basato su classi.
Un concetto fondamentale nella progettazione dell'interfaccia utente è quindi la corretta esposizione delle funzionalità sottostanti l'interfaccia stessa: in generale, infatti, l'utente impartirà alcuni comandi, o in termini OO manderà alcuni messaggi, attraverso alcuni elementi di interfaccia che dovremo rendere disponibili. Notiamo che non vi è alcuna differenza concettuale tra un'interfaccia a linea di comando, una a menu, una a bottoni: si tratta di diverse tecniche attraverso le quali l'utente invia dei messaggi al sistema. Anche la pressione di un tasto all'interno di un word processor è un mezzo per inviare un messaggio al sistema, che infatti reagirà in modo anche molto diverso a seconda del tasto premuto.
Ciò che cambia è la struttura dell'interazione: in una interfaccia a linea di comando, abbiamo il minimo di struttura, e quindi il massimo carico di lavoro per l'utente. Una interfaccia a menu successivi costituisce una struttura rigidamente gerarchica; un'interfaccia a menu pull-down e bottoni è spesso una mediazione tra le due tendenze, dove i menu impongono struttura gerarchica, ma l'utente ha maggiore flessibilità nel navigare la gerarchia. Questo significa però che il progettista deve organizzare la gerarchia in modo corretto, viceversa l'applicazione tenderà a combinare gli svantaggi delle soluzioni (complessità e rigidità) piuttosto che i loro vantaggi (flessibilità e semplicità).
Ha quindi senso chiedersi come i comandi esposti tramite menu e bottoni debbano essere organizzati, strutturati e raggruppati. È necessario seguire la struttura del modello object oriented, raggruppando ad esempio i servizi di ogni classe in un menu, ed esponendo il nome della classe come entry top-level? Oppure è meglio utilizzare un diverso ordine, non strettamente basato sul modello OO del dominio del problema? Come sempre, dipende dalla singola applicazione; possiamo però identificare due casi molto comuni, in cui esistono tecniche ben sperimentate:
1) menu popup attivati su un singolo oggetto: ad esempio, i popup che si attivano con il right-click in molte applicazioni Windows, o sugli oggetti grafici di OS/2. In questo caso, è più che indicato aderire al modello object oriented: selezionando un oggetto, l'utente indica implicitamente la classe dell'oggetto e quindi quali funzionalità debbano essere esposte. L'ereditarietà va comunque nascosta, ma in questo caso si tratta semplicemente di replicare le azioni disponibili su oggetti della classe base.
2) menu pull-down, bottoni: in questo caso, ha più senso organizzare secondo una astrazione di tipo funzionale: ad esempio, l'entry top-level "Edit" indica un insieme di azioni, poi dettagliate in "Copy", "Paste", eccetera. In questo caso, raggruppiamo funzionalità simili, anche se potenzialmente operanti su oggetti diversi. Spesso possiamo nascondere in questo modo il polimorfismo: funzionalità come Copy e Paste corrispondono a metodi virtuali nella visione del programmatore, e ad "azioni generali" nella visione dell'utente.
Naturalmente, è possibile avere un mix delle due tecniche: ad esempio, l'entry standard "File" indica una classe, e poi seguono i dettagli delle funzioni richiamabili su di essa, anche se si tratta di un menu pull-down; tuttavia, l'applicazione dei criteri su esposti porta molto spesso ad interfacce più intuitive. Infine, esiste un modo per esporre il polimorfismo a livello utente, semplificando peraltro la struttura dei menu: se l'entry top-level identifica un oggetto, non ripetete il nome dell'oggetto nella lista dei comandi; tornando all'esempio "File", avremo poi "Open" e "New" e non "Open File" e "New File". In questo modo, l'utente verrà abituato ad una serie di comandi standard, utilizzabili con lo stesso significato in altri contesti: ovvero, al fatto che oggetti diversi possono rispondere allo stesso comando, in modo "uguale" da un punto di vista astratto, pur se con effetti concreti leggermente diversi (ereditarietà e polimorfismo); naturalmente, dovete porre la dovuta attenzione a non esagerare in astrazione.
Notiamo che un'interfaccia pensata secondo criteri OOUI farà largo impiego di menu pop-up, attivati interagendo direttamente con i vari oggetti. Se da un lato questo modello di interazione auspica lo spostamento dell'attenzione utente, dall'applicazione verso i compiti da svolgere, e quindi verso gli oggetti da usare per svolgere tali compiti, il rischio è che l'interazione diventi "più complessa", proprio perché il modello ad oggetti è più facilmente implementabile come object-action. In questo caso è spesso necessario sviluppare un prototipo dell'interfaccia utente e verificarne direttamente l'usabilità da parte degli utilizzatori; naturalmente, un minimo di difficoltà è accettabile e spesso inevitabile, ma il successo di una applicazione è normalmente legato alla semplicità con la quale il nuovo utente riesce a padroneggiarla. Non di rado, ciò che sembra elegante e rapido per uno sviluppatore risulta scarno e complesso per l'utente finale: un prototipo, così come lo studio dei metodi e delle applicazioni già in uso presso l'utente, vi eviterà numerose modifiche successive. In questo caso, è possibile utilizzare uno strumento RAD, senza ripensamenti di sorta: il prototipo così sviluppato potrà essere riutilizzato o gettato, in funzione della qualità dello stesso, dei tempi disponibili, della vostra politica di sviluppo. In ogni caso, vedere l'utente all'opera è spesso uno spettacolo rivelatore: è difficile immaginare in quanti modi un utente finale "abusi" delle applicazioni, non appena vi è libertà di scelta; non a caso, i grandi produttori di software filmano gli "usability test" per poter rivedere le varie azioni svolte dall'utente, capire come cerca di ottenere alcuni risultati usando il programma, e così via.
Poiché difficilmente avremo a disposizione il budget di Microsoft per gli usability test, esistono due tecniche molto semplici per sfruttare i risultati delle grandi case produttrici, a costo pressoché nullo. La prima tecnica è, ovviamente, quella di "ispirarsi" all'interfaccia di una o più applicazioni già in uso presso l'utente; se si tratta di ambienti standard, come Windows, questo significa poco più che rispettare le linee guida del produttore. Tuttavia, talvolta anche la semplice somiglianza nei bottoni, lo stesso ordine nelle righe dei menu, la scelta di termini analoghi ad una applicazione già in uso (naturalmente, una apprezzata dall'utente!) potranno fare una grande differenza. Ancora di più , tuttavia, è ottenibile utilizzando le stesse tecniche di manipolazione degli oggetti: nuovamente, gli standard tendono ad estendersi rapidamente, ed ora azioni come (ad esempio) il drag-and-drop vengono considerate uno standard in molti sistemi. Ciò che è ancora possibile "carpire" da molte applicazioni è l'uso del metodo action-object o object-action nelle diverse situazioni, in modo da presentare all'utente degli scenari il più possibile familiari.
In questo senso, la seconda tecnica estende la prima alla sua conseguenza finale: immergere la nostra applicazione all'interno di una già utilizzata dall'utente. In tal caso, l'utente non abbandona mai la propria "applicazione favorita", ma si limita ad utilizzarne delle estensioni. Se in passato questo era visto come una "programmazione di serie b", spesso svolta utilizzando qualche macro-linguaggio fornito dal prodotto, oggi possiamo creare oggetti OLE2 o OpenDoc che possono essere inseriti, manipolati e memorizzati come parte dei normali documenti dell'utente. Considerando che una larghissima parte dei programmi ha come scopo finale la generazione di qualche forma di report, si può facilmente immaginare i possibili benefici conseguenti dall'integrazione con (ad esempio) un word processor avanzato. Sviluppare uno o più oggetti OLE2 anziché una applicazione stand-alone è al momento una strategia relativamente nuova, che tuttavia dovrebbe essere seriamente considerata prima di intraprendere lo sviluppo di un nuovo programma; chi desidera percorrere questa strada dovrà purtroppo affrontare temi di programmazione non proprio banali: un ottimo testo per chi voglia approfondire OLE2 è [7], per quanto chi voglia ottenere immediata soddisfazione, ed utilizzi un framework come MFC, possa iniziare con un testo più digeribile come [8] e solo in seguito, quando la necessità o la curiosità lo richiederanno, passare a titoli più corposi e dettagliati.

Consigli pratici In quanto segue, ho riportato alcuni spunti relativi al design delle interfacce utente, di stampo più pratico rispetto alle considerazioni precedenti.

The End
Ogni ciclo di lezioni ha una fine, ed "Object Oriented Technology" termina qui; nei mesi passati, abbiamo considerato i punti fondamentali dell'analisi e del design, e pur rimanendo a livello introduttivo spero di aver dato ai lettori una visione abbastanza chiara del mondo OOT.
Naturalmente, molti argomenti sono rimasti al di fuori della trattazione, come il testing del codice OO, i framework, le metriche sul design, la concorrenza, i sistemi distribuiti e gli strumenti CASE, e molti altri sono stati appena accennati, come gli aspetti di human-computer interaction visti in questa puntata. I lettori con un particolare interesse per un argomento relativo alle tecnologie object oriented possono comunque contattarmi via email: gli argomenti più importanti o richiesti saranno oggetto di futuri articoli di approfondimento.

Reader's Map
Molti visitatori che hanno letto
questo articolo hanno letto anche:

Bibliografia:
[1] Donald Norman: "The Design of Everyday Things", Doubleday, 1988. Tradotto come "La caffettiera del masochista", Editrice Giunti.
[2] Brenda Laurel: "The Art of Human-Computer Interaction", Addison-Wesley, 1990.
[3] Tony Rubin: "User Interface Design for Computer Systems", Johm Wiley & Sons, 1988.
[4] Ben Schneiderman: "Designing the User Interface", Addison-Wesley, 1987.
[5] D.D. Cowan, C.J.P. Lucena: "Abstract data views: a module interconnection concept to enhance design for reusability", Technical Report, Computer Science Department, University of Waterloo, Canada, 1993.
[6] Sotirowsky, Kruchten: "Implementing Dialogue Independence", IEEE Software, Novembre 1995.
[7] Kraig Brockshmidt: "Inside OLE 2, 2nd edition", Microsoft Press, 1995.
[8] Steve Holzner: "Il Manuale OLE 2.0", McGraw-Hill, 1994.
[9] Apple Computers Inc.: "Human Interface Guidelines", Addison Wesley, 1992.
[10] IBM Corporation: "Object Oriented Interface Design - IBM CUA Guidelines", Que Corporation, 1992.
[11] Microsoft Corporation: "The Windows Interface: An Application Design Guide", Microsoft Press, 1995.
[12] Maria R. Capucciati: "Putting Your Best Face Forward: Designing an Effettive User Interface", Microsoft System Journal, Febbraio 1993.
[13] Rainer Mauth: "Objects of Design", BYTE, Settembre 1995.

Biografia
Carlo Pescio (pescio@acm.org) svolge attività di consulenza in ambito internazionale nel campo delle tecnologie Object Oriented. Ha svolto la funzione di Software Architect in grandi progetti per importanti aziende europee e statunitensi. È incaricato della valutazione dei progetti dal Direttorato Generale della Comunità Europea come Esperto nei settori di Telematica e Biomedicina. È laureato in Scienze dell'Informazione ed è membro dell'ACM, dell'IEEE e della New York Academy of Sciences.