[Estratto dal capitolo 5 del libro di Shneiderman]
E' sensato stendere un progetto completo dell'interfaccia prima di iniziare ad implementarla, anche se e' certo che durante l'implementazione si dovra' cambiare qualcosa.
Quali informazioni descrivono lo stato corrente:
Che valore hanno queste variabili all'inizio.
Quali operazioni l'utente deve poter compiere attraverso l'interfaccia:
Di che parametri hanno bisogno queste operazioni.
Pensare in che modo l'utente invochera' queste operazioni tramite l'interfaccia (tramite bottone, menu'...) e in che modo immettera' i parametri.
L'esecuzione di queste operazioni come modifica lo stato dell'applicazione?
Le operazioni hanno sempre senso o ci sono stati in cui certe operazioni non hanno senso?
Ci sono vincoli sui parametri?
Pensare quali controlli sono necessari su operazioni invocate e parametri immessi.
Considerare se opportuno disabilitare i dispositivi corrispondenti a certe operazioni quando nello stato attuale non sono sensate.
Considerare quando e' necessario mostrare messaggi di errore o avvertimento e in che modo.
Quali azioni di ricovero bisogna prevedere? (es. in quali casi offrire possibilita' di tornare indietro con "undo")
Stabilito quali elementi servono nell'interfaccia, stenderne il layout (schizzo dell'interfaccia con indicazione dei dispositivi presenti e della loro collocazione).
Uno schizzo per ogni finestra (finestra principale, finestre di dialogo...). Gli elementi di interfaccia non permanentemente visibili (es. menu') sono descritti a parte.
Indicare anche, per ogni elemento attivo di interfaccia, l'operazione o la serie di operazioni compiute nella sua callback.
Individuare un insieme di attivita' applicative di interesse. Ogni attivita' si scompone in una serie di operazioni.
La complessita' della serie di operazioni necessaria
a compiere un'attivita' e' un indicatore per valutare l'interfaccia
relativamente ai fattori umani
(facilita' di apprendimento, comodita' d'uso, incidenza di errori).
Bisogna che le attivita' scelte per valutare l'interfaccia siano
significative!
Sono stati elaborati vari formalismi, per lo piu' grafici, per descrivere un'interfaccia progettata.
Schizzi di come appaiono le finestre dell'interfaccia, quali
dispositivi contengono e come dislocati.
Utili a fornire descrizione statica dell'apparenza dell'interfaccia.
Descrivono la struttura di menu' a cascata (menu' e sotto-menu').
Analoghi gli alberi per finestre di dialogo a cascata. In pratica descrivono la relazione di dipendenza funzionale tra finestre.
Un'altra variante etichetta gli archi con la frequenza (in percentuale) con cui l'utente compie quell'azione.
Versione modulare dei diagrammi di transizione.
Un grafo ad alto livello considera uno stato semplificato, dove non si tiene conto di certe variabili.
Un nodo del grafo ad alto livello si puo' espandere in un sottografo che fornisce descrizione piu' dettagliata dello stato, tenendo conto anche delle variabili che non erano state considerate al livello piu' alto.
Adatto a descrivere il comportamento di interfacce che usano manipolazione diretta.
Descrive le attivita' (task) in termini di sequenza di azioni dell'utente e relativo feedback fornito dall'applicazione.
Per ogni task si compila una tabella a tre colonne (azione dell'utente, feedback dell'interfaccia, cambiamento di stato del sistema). Nella tabella si elencano le azioni (con relative reazioni e cambiamenti di stato) che compongono il task, in ordine di tempo.
Per esprimere le azioni e i feedback si usa un linguaggio, che qui riportiamo semplificato (usiamo parole chiave in inglese, la versione originale usa simboli, piu' veloci da scrivere ma piu' difficili da ricordare).
Nel linguaggio posso denotare oggetti sui quali si compie l'azione o il feedback. Esempi:
E posso denotare azioni e feedback attuati su questi oggetti.
Esempi di azioni dell'utente:
Esempio: task per cancellare un file.
Vado sull'icona, premo tasto del mouse, trascino l'icona nel cestino, rilascio tasto del mouse.
La tabella viene:
User actions | Interface feedback | Internal state |
moteto[file], press | highlight[file], forall(highlight[file]): nohighlight[file] | selected_file = file |
moveto[x,y] | drag[outline(file)] | |
moveto[trash] | drag[outline(file)], highlight[trash] | |
release | erase[file], blink[trash] | selected_file = null |
In generale puo' bastare scrivere in linguaggio naturale non ambiguo e preciso. Cioe' mettere nella tabella la descrizione a parole del task che e' stata fatta prima.
Progettiamo un'interfaccia per un'applicazione "calcolatrice" con le operazioni binarie di addizione, sottrazione, moltiplicazione, divisione.
Per eseguire la prima operazione, l'utente immette il primo operando, poi l'operazione ed infine il secondo operando.
Per eseguire le operazioni successive l'utente immette solo l'operazione e il secondo operando, mentre il primo operando e' dato dal risultato dell'operazione precedente.
In qualsiasi momento l'utente puo' anche cancellare tutto e tornare allo stato iniziale, come se non avesse ancora eseguito nessuna operazione.
All'utente viene mostrato l'ultimo operando immesso o, se gia' eseguita, il risultato dell'operazione.
Variabili dell'applicazione:
Variabili dell'interfaccia:
Stato iniziale: Non esitono ne' i due operandi ne' l'operazione. Non visualizza nulla.
Dai vincoli imposti sono possibili solo 4 stati:
Immettere operazione.
Deve esistere almeno il primo operando.
Non deve esistere gia' un'operazione se non esiste il secondo operando.
Cioe' lo stato deve essere s1 o s3.
Se e' gia' impostata un'operazione ed
esiste il secondo operando, esegue l'operazione impostata,
il risultato diventa il nuovo primo operando,
questa diventa la nuova operazione,
il secondo operando resta indefinito.
Cioe' da s3 va in s2.
Se non e' impostata un'operazione e non esiste il secondo operando,
imposta l'operazione.
Cioe' da s1 va in s2.
Immettere un numero.
Non deve esistere ancora nessun operando, oppure deve esserci
un'operazione impostata.
Cioe' lo stato deve essere s0 o s2.
Se non esiste il primo operando, lo imposta.
Cioe' da s0 va in s1.
Altrimenti imposta il secondo operando.
Cioe' da s2 va in s3.
Azzerare.
Torna allo stato iniziale.
Possibile sempre, non ha molto senso se siamo gia' in stato iniziale.
Cioe' da s1,s2,s3 va in s0.
Finire operazione.
Devono esistere l'operazione ed entrambi gli operandi.
Cioe' lo stato deve essere s3.
Esegue l'operazione, il risultato diventa il nuovo primo
operando, l'operazione e il secondo operando restano indefiniti.
Cioe' va in stato s1.
Per non appesantire la notazione, le frecce dell'azione di azzeramento
sono state omesse.
Callback del campo di testo:
Callback dei bottoni:
In ogni momento sono disabilitati i dispositivi corrispondenti ad azioni non valide nello stato corrente: il campo di testo in stato s1 ed s3, i bottoni +, -, *, / in stato s0 ed s2, il bottone = in stato s0, s1, d2, il bottone C in stato s0.
Ogni volta che utente finisce di scrivere nel campo di testo, l'interfaccia controlla che sia stato scritto un numero. In caso contrario emette un "BIP" (o mostra un messaggio...) e rimane nello stato corrente.
Nuovo sotto-task per immissione di un numero:
inizia quando l'utente preme la prima cifra, continua
finche' l'utente preme cifre, termina quando l'utente preme
un bottone diverso da una cifra.
Dettagliamo gli stati s1 ed s3 in sotto-diagrammi che
descrivono la costruzione del numero cifra per cifra.
Ed analogamente (non e' il caso di disegnarlo) per s3.
La figura mostra solo la parte cambiata rispetto alla figura
precedente.
Callback dei bottoni-cifre: