langage de spécification d'interfaceslaurent.henocque.com/oldsite/doc/cours isl...

Post on 25-Dec-2019

1 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Langage de Spécification d'Interfaces

A l'attention des étudiants de l'ESIL-IRM.

Auteur : Laurent Henocquemis à jour en Janvier 2010

Licence Creative Commons

• Cette création est mise à disposition selon le Contrat Paternité-Partage des Conditions Initiales à l'Identique 2.0 France disponible en ligne

• http://creativecommons.org/licenses/by-sa/2.0/fr/

• ou par courrier postal à Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.

Situation

• Aucun langage dédié n'existe pour la spécification homme machine

• Les interfaces homme machine sont essentiellement spécifiées par prototypage

• Cette approche n'offre aucun secours dans le cas de grands projets comportant des dizaines ou des centaines d'écrans

• Il existe un réel besoin de support pour la spécification des IHM

Cas du Prototypage

• Un prototype peut être présenté au client et validé.

• Le prototype montre que ce qui est développé est compatible avec le besoin

• Le prototype ne peut pas illustrer des OBJECTIFS de développement - d'ergonomie notamment.

• Il manque alors un langage pour décrire CE QUE l'on veut implanter, avant de le faire effectivement.

ISL

• ISL = Interface State Language

• un langage pour décrire la dynamique des interfaces homme machine.

• ISL est fondé sur une extension du langage de spécification d'états défini par la méthode UML

• ISL existe sous deux formes: une forme textuelle, et une forme exécutable

La dynamique "interne" des IHM

• ISL décrit la dynamique interne des interfaces:

• Les actions et changements qui n'appellent pas des fonctions externes, mais sont limitées à l'interface elle même

• Par exemple, une interface cache ou dévoile fréquemment des fenêtres, change l'aspect de boutons, positionne ou enlève des callbacks sur des objets

Etats et Invariants

• Un état d’une interface est caractérisé par des invariants

• Les invariants définissent des valeurs attribuées à certaines propriétés d’objets de l’interface dans cet état

• Les invariants portent autant sur les attributs et ressources (couleur, visibilité) que sur les réponses aux événements (callbacks, listeners)

Transitions

• En ISL, une interface est décrite par ses états

• La dynamique interne est définie par des changements d'états, appelés transitions

• Ces transitions sont causées par des callbacks appliqués statiquement aux objets de l'interface

• La fraction de la dynamique de l'interface qui relève de changements d'états est ainsi décrite de façon statique

Une structure pour la dynamique

• L’organisation des états en ISL est structurée par l’existence d’états composites (selon le modèle UML State et les Statecharts de Harel)

• Les invariants spécifiés dans les états composites sont hérités et surchargés dans les sous états

• ISL donne donc une structure à la dynamique de l’interface

Bénéfices attendus

• spécifier rigoureusement l'interface, en garantissant l'absence de défauts d'aspects

• définir des abstractions utiles lors de projets réalisés en équipe

• accroître la dynamique et les qualités ergonomiques d'une interface à moindre coût

• spécifier l'automate d'interfaces très complexes

Rendre une spécification d'IHM exécutable

• Il est possible de développer une bibliothèque logicielle permettant de rendre exécutables des spécifications d'états

• Cela a été réalisé trois fois:

• Open Side (1991) : le langage des contextes, intégré au langage WIZ.

• Ilog Views (1995) : intégration d'une API de gestion d'états dans l'outil ILOG Views

• Aujourd’hui JavaStates(javastates.sourceforge.net)

Concepts fondateurs de

ISL

Détection d'état dans les interfaces

• Le développeur d’une interface peut être tenté de réaliser un code qui “teste” l’état de l’interface en de multiples endroits:

• cette fenêtre est elle ouverte?

• ce bouton est il actif?

• ce texte est il visible?

• On utilise alors les structures de contrôle habituelles if / switch - case etc...

Détection et Spécification

• On ne peut pas spécifier une interface définie en mode “détection”

• En effet, ce type d’approche est non réutilisable, et ne fournit pas d’abstractions pour une compréhension synthétique

• Même en développement, l’approche détection risque de conduire à des défaults d’aspects, et à des difficultés (nécessité fréquente de modifier des conditions dans tout le programme)

Etats nommés, en nombre fini

• ISL permet de nommer les états d'une interface, et ainsi de définir explicitement l'automate d'états finis que l'interface réalise

• A chaque état sont associées ses invariants: caractéristiques visuelles et de comportement

• Le langage graphique des diagrammes d’état UML est adapté à ce type de spécification (voir rappels de notation en annexe)

Invariant = Triple

• A chaque état sont associés autant d’invariants que nécessaire.

• Un invariant dans sa forme la plus simple est décrit par un triple <Objet,Ressource,Valeur>

• Un tel descripteur spécifie une ressource (couleur, position, visibilité...) où un comportement (callback, réponse à un événement) pour un objet donné

Dynamique VS Statique

• Détecter reflète une approche dynamique.

• Spécifier une interface sera plus facile et plus compréhensible en ayant un point de vue statique

• On décrit donc chaque état par ses invariants.

Une approche “Objet”

• La programmation objet dans les langages avancés permet de remplacer des tests par des appels de fonctions virtuelles.

• Nous allons considérer la spécification des IHM selon le même angle

• Avec un analogue dynamique des classes et des fonctions virtuelles

Un Langage D’états pour la spécification des IHM

• ISL ne propose aucune construction pour “détecter” dans quel état se trouve une interface, mais permet de définir un état courant, et les transitions

• Tous les descripteurs d'un état sont pris en compte lorsque cet état est sélectionné, et donc les changements correspondants effectués

Implantation des transitions

• Une implantation d'ISL peut effectuer des calculs une fois pour toutes lorsqu'un état donné est sélectionné.

• En reposant sur une implantation de ISL, une interface ne teste jamais si elle se trouve dans un état compatible avec la gestion d'un événement, et ne teste pas non plus quelle est la transition appropriée

• La transition appropriée a été installée sous forme d'un callback adéquat lors du changement d'état

Descripteurs de transitions

• Parmi les descripteurs dynamiques figurent "setState" qui permet des transitions explicites

• ISL suppose que l'intégrité des états n'est jamais violée : quelle que soit la manière d'y parvenir, un état se manifeste et se comporte exactement comme spécifié

• Lorsqu'on sort d'un état, les attributs (visuels et callbacks) des objets qui étaient modifiés par cet état sont remis à leur valeur d'origine, sauf si l'état cible exprime une condition distincte

Exemple

• Trois objets, créés comme 1, 2 et 3

• Nous sommes dans un état A ayant les invariants 1 et 2 (3 non mentionné reste bleu).

• On passe dans un état B décrit par 2, 3. ISL suppose que 1 est régénéré, donc l'état atteint est 1, 2, 3

• Si on revient en A, on retrouve 1, 2, 3

Création• Objets créés avec des couleurs par défaut

12

3

racine

BA

Etat racine root• L'interface possède un état “racine”, qui

définit pour 1 et 2 une valeur particulière de l'attribut couleur

12

3

racine

BA

On passe dans l'état A• Descripteurs 1 et 2 (3 reste bleu)

12

3racine

BA

On passe dans l'état B• Décrit par 2, 3. 1 hérite de l’état racine

12

3

racine

BA

On revient dans l'état Racine

• Les objets reprennent leurs attributs d'origine

12

3

racine

BA

Notions nécessaires

• Comme les diagrammes d'état de UML, ISL exploite le concept d'états composites et de leurs sous états

• Egalement, le parallélisme d'ensembles d'états est une notion nécessaire à la spécification des interfaces homme machine

Visibilité• En ISL, on ne décrit jamais un changement de

visibilité d'une fenêtre par un appel explicite aux fonctions "show()" et "hide()"

• L'attribut de visibilité est positionné par des descripteurs associés à des états

• Ainsi en changeant d'état on décrit implicitement les modifications associées de visibilité s'il y a lieu

• Au lieu de décrire par un programme les effets d'un changement d'état implicite, on décrit statiquement et explicitement les états par des invariants

Les causes des défauts d'aspect

• Le bénéfice de cette approche apparaît clairement quand un programmeur doit décider de ce qui doit arriver quand un changement d'état se produit, et qu'il y a beaucoup d'états, et beaucoup de ressources à modifier en conséquence

• est ce que cette fenêtre doit rester visible?

• La difficulté de cette simple question est la cause majeure de coûts de développement accrus, et de manque d'ergonomie

Références

• ISL repose sur les state charts de David Harel (article fondateur David Harel: On Visual Formalisms. CACM 31(5): 514-530 (1988),

• http://sunsite.informatik.rwth-aachen.de/dblp/db/indices/a-tree/h/Harel:David.html),

• popularisé par UML (Unified Modeling Language, http://www.rational.com/uml)

• un bon site en français : http://uml.free.fr

Extensions de UML requises par ISL

• la notion de descripteurs d'états (qui ne sont pas exactement atteints par les variables d'états)

• les sous ensembles d'états concurrents nommés

• une gestion des espaces de noms permettant d'isoler des symboles potentiellement en conflit

• la possibilité de rendre actif un état composite sans activer aucun de ses sous états : utile pour la spécification de dialogues et compatible avec le modèle de David Harel : chaque automate comporte un état "inactif" implicite

aspects techniques d’une

implantation

Etat A Etat B

p1:visible p1:invisibleb1:actif b1:rouge

Changement d'état: trois cas

Lors du passage de A vers B : •la couleur de b1 est mémorisée avant d'être modifiée, •l'état d'activité de b1 est remis à sa valeur anciennement mémorisée, •et la visibilité de p1 est seulement modifiée

b2 : blanc b3: rouge

Ressource Spécifiée dans l’Etat cible seul

• Une ressource n'est contrainte que dans l'état cible:

• Sa valeur avant le changement doit être sauvegardée.

• La nouvelle valeur est installée

Ressource Spécifiée dans l’ Etat source et état cible

• Une ressource est contrainte dans les deux états: source et cible

• La valeur mémorisée est conservée

• La valeur courante est modifiée conformément à l'état cible

Ressource Spécifiée dans l’ Etat source seul

• Une ressource n'est mentionnée que dans l'état source

• Sa valeur doit être restaurée à la valeur mémorisée.

Optimisations des implantations

• Ce mécanisme peut être optimisé de diverses manières :

• élimination de scintillements par l'utilisation du double buffering

• gestion intelligente des attributs de visibilité

• réduction au minimum des interventions sur l'interface, par des calculs ensemblistes reposant sur des tables de hachage

Syntaxe de ISL

Descripteurs

• Un descripteur portant sur la valeur qu'une propriété doit avoir dans un état particulier

• "propriété" doit être pris au sens large : attribut visuel (visibilité, couleur, ...), dynamique (callback ou listener, réaction à événement, interacteur, accélérateur clavier, etc...), mais aussi donnée membre d'une classe, variable globale etc...

• Les descripteurs ne portent que sur des objets nommés

Syntaxe

• La syntaxe pour les descripteurs est simple :

on "name" "resource" = "value" ;

• sauf ambiguïté, les guillemets, le signe égal et le point virgule sont optionnels

Hiérarchies d'objets

• Les interfaces font toujours apparaître des objets dans d'autres objets (par exemple dans les frames, ou dans les menus)

• On utilise une notation pointée : on "name0.name1.name2""resource"="value";

Hiérarchies d'objets

• On utilise aussi une notation imbriquée

on "name0" {

on "name1" {

on "name2" "resource" = "value" ;

}

}

Alias

• Dans de nombreux cas, la lisibilité des spécifications peut être améliorée si l'on identifie les objets graphiques par leur nom et leur type.

• Ainsi, "JFrame", "JPanel", "XmPuxhButton" sont des alias possibles au mot clef "object", mais également leurs versions indépendantes des bibliothèques "frame", "panel", "button" etc...

• Egalement, les versions localisées de ces mots sont utilisables : "fenêtre", "panneau", "bouton" etc...

Exemples de descripteurs

panel panel1 on button1 background = blue;

panel panel1 on button1 label = "a label with spaces and a newline

in it";

panel panel2 visible = true;

panel panel3 on MenuBar.File.Open

callback = OpenFile(); // a menu item

Syntaxe XPath

• On peut présenter les descripteurs en utilisant la syntaxe Xpath (analogue à la syntaxe des fichiers ‘app_defaults’ de XwindowPanel1.Button1.background=blue;

Panel1*Jbutton.background:transparent;

• Dans ce ‘style’, on peut utiliser librement les noms d’instances et les noms de types.

Le contexte

• L'objet le plus récemment spécifié sert de contexte pour tous les descripteurs suivants.on panel1

! visibility = true; // appliqué à panel1

background = blue; // également

! on button1

! ! foreground = red; //panel1.button1 ! ! callback = quit();// également !

! on button2

! ! foreground= blue; //panel1.button2

Le contexte (2)• Les mots clé identifiant des objets de haut niveau

(panel, frame, etc...) sont non enchâssés par défaut. En cas d'ambiguïté, on utilise des accoladeson panel1 {

visibility = true; // appliqué à panel1

background = blue; // également

on panel2

! ! foreground = red; //panel1.panel2

}

on panel3 background = green;

API Javastates

State on_(Object ...); // le contexte

State on_(String); // le contexte

State_on(); // ferme le contexte

State req(String attribute,Object value);

State req(Object cxt,String a,Object v);

• Exemple:on_(saveItem, closeItem).

req("Enabled", true).

_on().

Etats

• Un état est un conteneur pour des descripteurs et d'autres états appelés ses sous états

• Quand un état devient actif, tous ses descripteurs sont appliqués

• Tous les sous états héritent les descripteurs de leurs états englobants

Syntaxe

• La syntaxe des états est la suivante:state "nom d'état" {

! liste de descripteurs et de sous états

}

• Les accolades sont obligatoires, même si l'état ne contient ni descripteur ni sous état (ce qui est un cas valide)

Exemple

state A

{

! panel MyMainWindow

! visibility = true;

! object ButtonQuit

! ! visibility = true;

! ! callback = pleaseLeaveSoftware();!

}

API Javastates

STATE_("print").

req(exitItem, "Background", Color.RED).

req(printItem, "Enabled", false).

req(printFrame, "Visible", true).

_STATE("print").

Sous états

• Un sous état est seulement une déclaration d'état imbriquée, avec ses propres descripteurs et sous états

• Les sous états héritent des descripteurs de leurs super états

• Un descripteur peut être surchargé par un sous état

Syntaxe imbriquée

state A {

! ... // descripteurs de A

! state B_in_A {

! ! ... // descripteurs de B_in_A

! ! state C_in_B_in_A!{

! ! ! ... // descripteurs de C_in_B_in_A

! ! }

! }

! state D_in_A ! {

! ! ... // descripteurs de D_in_A

! }

}

Exemple Javastates

STATE_("print").

...

STATE_("confirm").

...

_STATE("confirm").

_STATE("print").

Syntaxe extraite

• Il peut être difficile de maintenir des spécifications faisant apparaître un trop grand nombre d'imbrications d'états.

• Egalement, il est parfois nécessaire de décrire des fragments d'une spécification dans des fichiers séparés, ce qui exclut la syntaxe imbriquéestate A { ... }

state B_in_A : A { ... }

state C_in_B_in_A : B_in_A { ... }

state D_in_A : A { ... }

Héritage• Les descripteurs d'un état sont hérités par tous les

sous états, et peuvent être localement surchargésstate main {

! panel "main panel" !

! ! background = black;

! ! visibility = true;

}

state derived : main {

! panel "main panel"

! ! // background = black (hérité)

! ! visibility = false; // surchargé

}

Exercice

• Décrire les états d'une application à deux fenêtres, dont l'une est une fenêtre principale, et l'autre une fenêtre d'impression par exemple

Concurrence et sous régions

Concurrence et Sous régions

• Par défaut, les sous états sont exclusifs

• Les interfaces modernes font intervenir beaucoup de parallélisme dans les activités offertes à l’utilisateur

• Par exemple, l'affichage d'une aide, ou le choix d'une langue pour les labels, sont indépendantes des autres fonctions d'une interface, et correspondent à des états concurrents

Notation pour la concurrence

• Par définition, deux sous états non imbriqués sont mutuellement exclusifs : au plus un est actif à la fois

• Pour décrire le parallélisme, il est nécessaire de permettre l'existence simultanée de plusieurs groupes de sous états, concurrents entre les groupes, mais exclusifs en leur sein

• Ces groupes s'appellent des "sous régions" en UML (subregions)

Syntaxe

state A {

...

region R {

... // état racine (“root”) de R

state B { ... }

}

}

API Javastates

STATE_("edit").

// ...

REGION_("Find").

set(findItem,"open").

STATE_("open").

// ...

_STATE("open").

_REGION("Find").

_STATE(“edit”).

Syntaxe Extraite

• state child : parent [ region name ] { ... }

• Cette déclaration définit "child" comme un sous état de "parent", figurant dans la sous région "name"

• state child : parent { ... } déclare “child” dans (la sous région par défaut de) l’état parent

Sous régions : Exemple

• Chaque sous région est indépendante (concurrente) des autres

• Les états de chaque sous région sont mutuellement exclusifsstate global {

! region locale {} // definitions de language

! region function {} // états fonctionnels

! region help! {} // aide

}

Conflits de concurrence

• Deux descripteurs sont incompatibles seulement s'ils sélectionnent des valeurs différentes pour le même attribut du même objet

• C'est une erreur que deux états concurrents formulent des descripteurs incompatibles

• L’API produit des résultats imprévisibles dans ce cas

Commentaires

• Comme les états A et B peuvent être actifs simultanément, la spécification est erronée

• Le parallélisme n'a de sens que pour des descripteurs portant sur des données séparées

• Toutefois, des états concurrents peuvent agir sur des attributs différents du même objet

• Par exemple, deux états concurrents peuvent agir l'un sur la couleur de fond, et l'autre sur le texte du label

Exemple de Conflit de concurrence

state root { ... }

state A : root [functionality] {

! panel panel1 visibility = true;

}

state B : root [ help ] {

! panel helpPanel visibility = true; // ok

! panel panel1 visibility = false; // ko

}

Etat Initial

• Chaque sous groupe d'un état possède un état initial. En UML cet état initial est décrit par une pastille noire

• Par défaut, l'état initial est l’état racine de la région

• Lorsque une interface est placée dans un état donné, tous les états initiaux de toutes ses sous régions sont activés

Syntaxe

• On peut spécifier un état particulier comme état initial d'un sous groupe donnéstate A {

region A_1 initial B {

...

state B {}

}

}

Rôle des états initiaux

• Cette fonctionnalité est utile comme une abstraction. Un état appelé "impression" peut être utilisé par l'ensemble d'une équipe, ou identifié tôt dans le processus de spécification, sans préjuger de son implantation finale.

• On peut aussi admettre, à l'instar des fichiers de défaults de XWindow, qu'un utilisateur avancé puisse changer des états initiaux d'une interface qu'il utilise souvent

Exemplestate abstract_global {!

! region "default" initial concreteB1 {

! state concreteA1 {...}

! state concreteB1 {...}

}

! subregion "help" initial abstractB2 {

! state concreteA2 {...}

! state abstractB2 {

! ! subregion "default" initial concrete_deep_B {

! ! state concrete_deep_A{...}

! ! state concrete_deep_B{...}

! }

}

}

Commentaires sur les états initiaux

• Le concepteur d'un état peut contrôler l'établissement automatique d'un sous état

• Les autres parties de la spécification peuvent connaître l'abstraction en ignorant les détails de la spécification

• Le concepteur de l'abstraction a une totale liberté pour définir la structure réelle de l'abstraction

Espaces de noms

• Les états initiaux permettent on l’a vu de séparer le travail de spécification entre plusieurs ingénieurs.

• Le besoin d'éviter des conflits de noms apparaît spontanément dans ce cadre.

• ISL considère l’état racine de la spécification ainsi que chaque sous région, comme un espace de noms

Conventions de nommage

• Les noms d'états sont uniques dans un espace de noms

• Les noms des régions sont uniques dans la spécification

• Désigner un état ambigu se fait en utilisant le nom de sa région en préfixe (region::state).

Désigner les états

• Quand un état est désigné dans un descripteur par un nom simple, l'état portant ce nom est cherché dans la région qui déclare le descripteur, puis récursivement en remontant les régions

• Dans tous les autres cas, il convient d'employer une notation non ambiguë mentionnant les noms de packages de façon explicite

Les callbacks setState et reset

• ISL prévoit deux callbacks prédéfinis pour les changements d'états

• Les fonctions correspondantes sont utilisées comme "valeurs" de descripteurs de callbacks

• exempleon buttonOK callback = setState(astate);

on buttonCancel callback = reset();

Définitions

• setState positionne l'état courant

• ce n'est pas une erreur si cet état est déjà actif

• si c'est le cas, les sous états actifs sont abandonnés, et l'on procède comme pour le premier établissement de l'état (i.e. relativement aux états initiaux)

• toutefois les sous régions de l’état ne sont pas affectées

Le callback reset

• Le callback “reset” permet de replacer une région dans son état initial (par défaut l’état racine)

Exemple

region confirm {

! on btn1 callback = setState(doconfirm);

! ...

! state doconfirm ! {

on panelConfirm visibility=true

! ! on btnOk callback = // do it!

! ! on btnCancel callback = reset();

! ! ...

! }

}

Exemple API Javastates

REGION_("Find").

set(findItem,"open"). // click to set state “open”

STATE_("open").

req(findFrame, "Visible", true).

reset(findItem). // click to reset region

met(findButton, ...).

_STATE("open").

_REGION("Find").

Compléments d’API Javastates

Création de l’état racine d’une spécification

• La fonction statique MAIN(String) de la classe State crée un état racine et la région associée.

• La région porte le nom fourni en argument.

• L’état racine s’appelle “root”

State mainState = State.MAIN_("Main").

// ...

_MAIN();

Création d’un requirement de type Listener

• Javastates crée des requirements pour toutes les classes supportant une API normalisée du type “Listener” de Java Swing.

• Pour une listener de classe XyzListener, les objets cibles doivent supporter l’api addXyzListener(...), removeXyzListener(...) et getXyzListeners()

La fonction lis() Exemple

lis(helpFrame,

WindowListener.class,

new WindowAdapter(){

public void

windowClosing(WindowEvent e){

try {State.getRegion("Help").reset();

} catch(Exception f){...}}

}).

Eléments de Méthode

Bonne utilisation de l’héritage

Etats composites : un support pour l'abstraction• Il est souvent utile de considérer des états

comme des abstractions pour d'autres

• Par exemple, le mode “MoveResize”

• On peut définir un état "MoveResize", abstraction pour plusieurs sous états comme : "NothingSelected", "ButtonDownInsideObject", " ButtonDownInsideResizingBox ".

• Certaines caractéristiques de l'interface sont communes à ces trois états (icône, transitions vers d'autres états etc...)

Factoriser les descripteurs

• L'ensemble des descripteurs communs à des états voisins peuvent être adéquatement groupés dans un super état abstrait

• L'utilisation d'états initiaux permet de garantir qu'un tel état abstrait ne soit jamais actif isolément

Interface d’Editeur de

TextesExemple

Définir une interface avec des états

Objectif : exemple 1

• On imagine l'interface d'un éditeur simplifié.

• Il a pour fonctions l'édition bien sûr, et aussi la sauvegarde, l'ouverture de fichier, l'impression et la recherche dans le texte appelées:

• <edit>, <load>, <save>, <find> et <print>.

Analyse du module fonctionnel

• <edit> est une fonction interne de l'interface, ainsi que <find>. Les fonctions <save>, <load>, et <print> sont des appels au système, qui est perçu comme le module fonctionnel interfacé

• Il n'y a pas de protocole particulier pour <save>, <load>, et <print>, ces trois opérations peuvent être effectuées dans n'importe quel ordre

• (on ignore ici le fait que la sauvegarde puisse dans un programme intelligent rester impossible tant que rien n'a été édité)

Identifier les fenêtres principales

• L'application a un panneau principal appelé "mainPanel".

• On y trouve la barre de menu et la zone de travail

• <save> et <load> partagent une fenêtre de sélection de fichier appelée "fileSelector".

• <print> prend ses paramètres grâce à un panneau "printParameters"

• <find> requiert un panneau "findPanel".

Concevoir l'automate

• <edit> est concurrente avec le reste des fonctions

• <find> n'a pas d'impact sur les données, et est également indépendante des autres fonctions

• <save> et <print> sont concurrentes entre elles: leurs fenêtres peuvent être simultanément visibles

Concevoir l'automate

• <load> est exclusive de <save> et <print>.

• quand l'utilisateur initie un dialogue <load>, les dialogues <print> et <save> disparaissent s'ils étaient visibles.

• <load> et <save> étant exclusifs, ces états peuvent partager la même fenêtre de sélection de fichiers.

Développer la structure générale des états

state package editor {!

// the default subregion is kept for standard editor functionalities!

! panel mainPanel visible = true! !

! ! object menuBar.File.Open callback=setState(load);! !

! ! object menuBar.File.Save callback=setState(save);! !

! ! object menuBar.File.Print callback=setState(print);

! ! object menuBar.Edit.Find callback =setState(find);

! panel fileSelector visible = false!

panel printParameters visible = false!

! panel findPanel visible = false

}

<Find>, <Load>state package editor { ...

! subregion find_subregion; !

! state find [ find_subregion ] {!

! ! panel findPanel visible = true!

! ! object close callback = leaveState(find);

object search callback = doSearch();

! }!

! subregion lps_subregion; !

! state load : editor [ lps_subregion ] {!

! ! panel fileSelector visible = true!

object close callback = leaveState(load);!

object ok callback = loadFromFile();!

! }

saveOrPrint (abstraction)

state saveOrPrint:editor [ lps_subregion ] {

// default subregion not used, abstract state !

! ! state save [ save_subregion ] {

panel fileSelector visible = true

object close callback = leaveState(save);! object ok callback = saveToFile();!!

}

! ! state print [ print_subregion ] {

panel printParameters visible = true

object close callback = leaveState(print);! object ok callback = print();

! ! }!

}

Commentaires

• L'exemple à ce stade illustre de quelle manière des états abstraits peuvent être utilisés pour grouper des informations communes à plusieurs sous états

• En particulier, cela sert à définir une fois pour toutes de valeurs pas défaut, chaque sous état étant défini par différence avec ses super états

• Il est d'usage que l'état racine soit qualifié de "package" pour signifier qu'il constitue un espace de noms

Eléments de méthodE

Que doit on faire?• utiliser des schémas d'interaction bien connus,

et singer les interfaces que l'on préfère

• définir des standards de spécification/conception d'interfaces que les programmeurs doivent respecter : le dialogue de confirmation

• Lier précisément les artéfacts visuels et la sémantique : le même "signe" (la couleur "rouge" d'un bouton par exemple) doit avoir une signification constante dans l'interface

• préférer le Undo à la confirmation

Que doit on éviter?

• de court circuiter les états avec des callbacks ayant des effets de bord

• d' utiliser plus qu'un nombre limité (trois) de signes visuels percutants dans une fenêtre

• d'utiliser trop de couleurs

• de faire bouger les objets

• de faire une interface dont la moyenne des couleurs soit trop éloignée du gris

Que peut on raisonnablement faire?

• réutiliser la même fenêtre dans plusieurs états exclusifs

• par exemple : la fenêtre de sélection de fichiers

Processus de Mise en Oeuvre

de ISL

Processus de spécification

• ISL ne modifie pas les phases amont de la spécification d'une interface

• Il permet de décrire le comportement des panneaux de l'interface sans programmer

• La spécification des panneaux demeure une tâche fondamentale

• Nous proposons un processus simplifié de mise en œuvre de la méthode

Décrire le protocole applicatif

• Le module fonctionnel interfacé obéit à un protocole : les appels de fonctions ne sont pas libres et indépendants (comme pour une pile)

• L'interface aura pour rôle de donner accès à ce(s) module(s) fonctionnel(s) en guidant l'utilisateur ou en évitant ses erreurs

• La première chose à faire est de décrire l'automate des modules fonctionnels (exemple winamp)

Style de programmation

• Un module fonctionnel ne corrige généralement pas les erreurs mieux que par le lancement d'exceptions

• Une erreur d'interaction provoquerait un arrêt brutal.

• Il est déraisonnable de dupliquer la logique de filtrage/correction d'erreurs

• C’est donc la responsabilité de l'interface.

Machine d'états du module fonctionnel

• On décrit un protocole avec des diagrammes d'états UML

• évènements <-> appels de fonction

• Cet automate sert de guide pour la spécification de l'automate de l'interface

• arguments de fonctions = données saisies dans l'interface

Sémantique de l'interface

• Certaines des fonctions d'un module fonctionnel sont essentielles, d'autres plus auxiliaires

• les fonctions essentielles sont celles aui correspondent à la raison d'être du programme

• ex : login sur db / requête sur db

• Identifier ces fonctions c'est isoler la "sémantique" de l'interface

Définir les panneaux principaux

• C'est la première étape

• Leur contenu dépend du protocole fonctionnel, et des données à fournir en paramètre

• Les choix faits pour les gadgets reposent sur la charte graphique

Définir l'automate global

• Après la spécification des panneaux vient la spécification des états principaux de l'interface.

• Les états sont identifiés, hiérarchisés, et la question de leur concurrence est posée

Définir les packages

• Eventuellement, des états peuvent être sélectionnés pour isoler un espace de noms de l'extérieur

• Cette fonctionnalité est essentielle pour développer des automates sans risques de conflit de nommage

• ex : "confirmer" / "quitter"

Schémas d'états

• On peut aussi réutiliser un schéma d'états préexistant (par exemple pour l'interaction de type confirmation/annulation

• L'automate développé doit respecter le standard syntaxique. Un moyen de le garantir est précisément de réutiliser des schémas d'interaction.

• Le moyen le plus habituel de le faire est de réutiliser des objets de niveau dialogue (par exemple la boite de sélection de fichiers)

Compléter l'automate

• définir la hiérarchie

• définir les callbacks de transition

• décider des options de visibilité de panneaux

Raffiner l'automate

• panneaux auxiliaires (messages...)

• états auxiliaires, transitions associées, confirmations, etc

• on continue à respecter la charte syntaxique

Décorer l'interface

• On améliore l'ergonomie :

• artéfacts visuels

• compléments d'information (messages, tooltips etc)

• l'interface peut informer l'utilisateur sur

• l'état courant

• l'objectif visé (imprimer, faxer, enregistrer, éditer, etc...

Augmenter le nombre de transitions

• On ajoute des transitions qui fournissent des raccourcis, afin d'améliorer la navigation

• boutons bascule

• fenêtres qui provoquent lors d'un click où passage au premier plan (mapping) le changement d'état vers ce à quoi elles servent

• On peut viser à couvrir le plus possible du graphe d'états de façon à le rendre quasi complet

Définir des Standards

Niveau lexical

• Style des polices de caractères

• apparence des boutons ok, quit, cancel etc...

• apparence des labels

• dimensions et positions des différentes catégories de panneaux

Niveau syntaxique

• Chaque objectif peut être atteint par différentes séquences d'interaction

• Chaque séquence fait intervenir des éléments du niveau lexical

• La réunion de ces séquences est le protocole de l'interface

• Pour garantir l'utilisabilité de l'interface, les séquences doivent être les mêmes pour des opérations similaires

Exemple

• La séquence confirmation / annulation

• fenêtre de confirmation ?

• click supplémentaire sur le bouton après avoir changé le label?

• Sélection multi niveau

• cascade de popup menus ?

• affichage répété de listes intermédiaires?

• utilisation d'objet "tree"

Schémas Interaction Patterns

• Un produit utile de la charte syntaxique est la définition de schémas d'automates pouvant être réutilisés

• C'est une manière simple d'améliorer les chances de respects de cette charte

Le niveau sémantique

• L'utilisateur poursuit toujours un ou plusieurs objectifs simultanés

• l'interface doit donner des signes visibles à tout moment de ce qui est en cours

• Ces signes doivent être homogènes dans toutes les situations

Le niveau sémantique

• On doit pour cela lister les fonctions essentielles, et leur associer des artéfacts visuels, si possible compatibles avec la concurrence, si les fonctions peuvent être accédées simultanément

• L'interface devrait en permanence répondre visuellement à la question "pourquoi suis-je là"

• C'est également un point requis par les exigences de sécurité dans le cas des grandes interfaces

Séquences obligatoires / états

• On appelle séquence obligatoire une séquence d'interaction minimale pour atteindre uin objectif

• par exemple pour imprimer

• Chaque étape correspond à au moins un état de l'interface

• On peut se demander si ces états sont imbriqués ou frères (ils sont bien sûr exclusifs)

Préférer l'héritage

• Les états d'une séquence diffèrent généralement peu de l'un à l'autre, si ce n'est pas quelques visibilités de panneaux

• Cela peut être une bonne idée de laisser les fenêtres intermédiaires visibles, afin de permettre un retour en arrière direct

Autres arguments

• L'utilisateur / le programmeur ont souvent l'impression de s'enfoncer dans un processus en suivant une séquence

• les états imbriqués en fournissent une bonne métaphore

• les choix réalisés à chaque étape correspondent à des sous états exclusifs

Modalité

• La modalité est positive quand l'utilisateur gagne des possibilités suite à un changement d'état

• Elle est négative dans le cas inverse

• Les fenêtres modales (bloquantes) doivent être évitées à tout prix car anti-ergonomiques

• Ces fenêtres implantent la forme la plus forte de modalité négative, qui interdit généralement des actions possibles sans danger

Cas possibles de fenêtres modales

• Dans les systèmes critiques (monitoring de centrale nucléaire par ex) si l'application doit recevoir une réponse à tout prix

• Dans une file de séquence quand les paramètres de la phase précédent ne peuvent plus être changés

Modalité négative et étatsstate!print {

! panel parameters visible = true;

! ! object print callback = setState(confirm);

! panel confirmation visible = false;

}

state confirm : print {

! panel confirmation

! ! visible = true; ! // positive modality

! ! object close callback = setState(print);

! ! object apply callback = doPrint();

! panel parameters

! ! visible = false; // negative modality

}

Raffiner une SpécificationExemple de

l’Editeur de textesExemple :

Raffiner une interface avec des états

Objectif (Rappels)

• On reprend l'interface de l'éditeur simplifié vu précédemment

• Il a pour fonctions l'édition bien sûr, et aussi la sauvegarde, l'ouverture de fichier, l'impression et la recherche dans le texte appelées:

• <edit>, <load>, <save>, <find> et <print>.

• On illustre le processus décrit précédemment

Analyse du module fonctionnel

• <edit> est une fonction interne de l'interface, ainsi que <find>. Les fonctions <save>, <load>, et <print> sont des appels systèmes, perçu comme le module fonctionnel

• Il n'y a pas de protocole pour <save>, <load>, et <print>.

Identifier les fenêtres principales

• L'application a un panneau principal appelé "mainPanel".

• On y trouve la barre de menu et la zone de travail

• <save> et <load> partagent une fenêtre de sélection de fichier appelée "fileSelector".

• <print> prend ses paramètres grâce à un panneau "printParameters"

• <find> requiert un panneau "findPanel".

Concevoir l'automate

• <edit> est concurrente avec le reste des fonctions

• <find> est indépendante des autres fonctions

• <save> et <print> sont concurrentes entre elles

• <load> est exclusive de <save> et <print>.

• quand l'utilisateur initie un dialogue <load>, les dialogues <print> et <save> disparaissent

• <load> et <save> étant exclusifs, ces états peuvent partager la même fenêtre de sélection de fichiers.

Développer la structure générale des états

state package editor {!

// the default subregion is kept for standard editor functionalities!

! panel mainPanel visible = true!!

! ! object menuBar.File.Open callback = ! ! ! setState(load);! !

! ! object menuBar.File.Save callback = ! ! ! setState(save);! !

! ! object menuBar.File.Print callback =

! ! ! setState(print);! !

! ! object menuBar.Edit.Find callback = ! ! ! setState(find);

! panel fileSelector ! visible = false! panel printParameters visible = false!

! panel findPanel visible = false

}

<Find>, <Load>

state package editor { ...

! subregion find_subregion; //////////////////////////////////!

! state find [ find_subregion ] {!

! ! panel findPanel visible = true!

! ! object close callback = leaveState(find);! object search callback = doSearch(find);

! }!

! subregion lps_subregion; //////////////////////////////////!

! state load : editor [ lps_subregion ] {!

! ! panel fileSelector visible = true!

! ! object close callback = leaveState(load);! object ok callback = loadFromFile();!

! }

saveOrPrint (abstraction)

state saveOrPrint:editor [ lps_subregion ] {!! // default subregion not used, abstract state !

! ! state save [ save_subregion ] {! ! ! ! panel fileSelector visible = true!! ! object close callback = leaveState(save);! object ok callback = saveToFile();! ! }

! ! state print [ print_subregion ] {!! ! panel printParameters visible = true!! object close callback = leaveState(print);! object ok callback = print();

! ! }!

}

Commentaires

• L'exemple à ce stade illustre de quelle manière des états abstraits peuvent être utilisés pour grouper des informations communes à plusieurs sous états

• En particulier, cela sert à définir une fois pour toutes de valeurs pas défaut, chaque sous état étant défini par différence avec ses super états

• Il est d'usage que l'état racine soit qualifié de "package" pour signifier qu'il constitue un espace de noms

Raffiner la structure de l'automate

• On ajoute une fenêtre de confirmation pour l'impression

• Cela requiert l'ajout d'une fenêtre, et d'un état qui contrôle sa visibilité.

• C'est illustré comme suit :

Dialogue de confirmation

state print: saveOrPrint [ print_subregion ] {

! panel printConfirm visible = false

! panel printParameters visible = true! !

! button close callback = leaveState(print);

! button ok callback = setState(printConfirm);

! state printConfirm {!

! ! panel printConfirm visible = true

! ! object ok callback = print();!! !

! ! object cancel callback = leaveState(print);

! }

}

Augmenter le nombre de Transitions

• L'exemple précédent introduit une fenêtre de confirmation mais ne masque pas la fenêtre de paramètres . Cela offre plusieurs avantages :

• 1- la fenêtre de paramètres reste ouverte de sorte que l'on sache ce que l'on est en train de confirmer

• 2 - cela facilite le retour à cette fenêtre de paramètres

• 3 - cela permet de donner au bouton "print" une fonction de bascule, particulièrement ergonomique

Raffiner

• On adapte l'automate en modifiant le sous état "printConfirm" de "print".

• Le bouton print, au lieu de lancer l'impression, passe dans l'état "printConfirm".

• Dans l'état printConfirm, ce même bouton a pour fonction de quitter l'état printConfirm (et donc dans cet exemple simple de revenir à l'état print)

Ajout de Transitionsstate print : saveOrPrint [ print_subregion ] {

panel printConfirm visible = false

panel printParameters visible = true!!

! object close cb = leaveState(print);

! object ok cb = setState(printConfirm);!

state printConfirm {! ! ! !

! panel printConfirm visible = true!!

object ok cb = print();! ! !

object cancel cb = leaveState(print);

panel printParameters!! ! !

object close cb = leaveState(printConfirm);

object ok cb = leaveState(printConfirm);!

! }

}

Conclusion

• ISL est un langage puissant pour la spécification d'interfaces

• ISL étend les diagrammes d'états de UML

• ISL peut être rendu exécutable

Système de Commande de Locomotive

ExempleDéfinir une interface avec des états

Objectif : exemple 2

• Vous êtes le concepteur interface pour la commande d'une locomotive à vapeur

• La spécification doit décrire la dynamique de cette interface.

• Certains états sont concrets, et ont pour vocation d'être activés

• D'autres sont abstraits et groupent des descripteurs partagés sans avoir pour vocation à être activés isolément

Hypothèses

• La machine peut être garée (à l'arrêt moteur froid)

• à l'arrêt,

• chaudière en chauffe,

• chaudière chaude,

• en mouvement

• chaudière en cours de refroidissement

• chaudière froide

Globalstate global { ! // abstract

! // spécifie la barre de menus, les callbacks de menus sont positionnés, mais insensitifs

! initial Parked;

! panel mainControlPanel visibility = true;

! ! object MenuBar.Engine.Operate

! ! ! callback = setState(InOperation);

! ! ! sensitivity = false;

! ! object MenuBar.Engine.Park

! ! ! callback = setState(Parked);

! ! ! sensitivity = false; !

! panel mainEnginePanel visibility = false;

}

Parked

• C'est l'état initial

state Parked : global {

! panel mainControlPanel

! ! object MenuBar.Engine.Operate sensitivity = true;

}

InOperation

state InOperation : global { // abstract

! initial BoilerHeatingAndStopped;

! panel mainEnginePanel!

! ! visibility = true;

! ! object StartButton visibility = false;

! ! object StopButton visibility = false;

! ! object TemperatureHotEnough ! visibility = false;

! ! object TemperatureColdEnough visibility = false;

! ! object CoolBoiler visibility = false;

! ! object Quit ! visibility = false;

}

BoilerHeatingAndStopped

state BoilerHeatingAndStopped:InOperation {

! ! // le premier état atteint lors de

! ! // la mise en opération

! ! panel mainEnginePanel

! ! object TemperatureHotEnough

! ! ! visibility = true;

! ! ! callback = setState(BoilerHot);

}

BoilerHotstate BoilerHot:InOperation { // abstract

! initial Stopped;

! ! state Stopped {

! ! // on peut démarrer ou refroidir

! ! panel mainEnginePanel

! ! object CoolBoiler

! ! visibility = true;

callback = setState(BoilerCooling);

! ! object StartButton

! ! visibility = true;

callback = setState(Running);

! ! }

}

Runningstate Running:BoilerHot {

! // seule transition possible vers "stopped"

! panel mainEnginePanel

! ! object StartButton

! ! ! visibility = false;

! ! }

! ! object StopButton

! ! ! visibility = true;

! ! ! callback = setState(Stopped);

! ! }

! }

BoilerCooling

state BoilerCooling:inOperation {

! ! // on attend que la température soit assez

! ! // basse pour garer la locomotive

! ! // la décision est sous contrôle de

! ! // l'utilisateur (click "ok, cold enough")

! ! panel mainEnginePanel

! ! ! object TemperatureColdEnough

! ! ! visibility = true

! ! ! callback = setState(BoilerCold);

! }

BoilerCold:InOperation

state BoilerCold:InOperation {

! ! // la machine peut être garée

! ! panel mainControlPanel

! ! ! object MenuBar.Engine.Park ! ! ! !! sensitivity = true;

! ! panel mainEnginePanel

! ! ! visibility = true

! ! ! object Quit callback = !

! ! ! ! setState(Parked);

! }

Combiner macro et micro automates

• ISL est utile pour décrire des états de haut niveau dans une IHM: visibilités de fenêtres, callbacks,etc...

• Les fonctionnalités d'une machine d'états finis peuvent être également utilisées pour décrire des automates plus élémentaires : sélection, dimensionnement, déplacement d'objets 2D...

• Tous les aspects de la dynamique interne d'une IHM peuvent être traduits en terme d'automates d'états finis (généralement, on utilise des composants prédéfinis qui gèrent déjà en interne ces automates)

Efficacité

• Une implantation de ISL peut être efficace

• quand on quitte un état A pour un état B, on reste dans un état commun le plus spécifique C

• l'algorithme pour gérer le changement d'état peut être linéaire sur le nombre de spécifications d'état applicables à B et A qui ne sont pas héritées de C

Applications

• Des changements subtils dans l'interface peuvent être modélisés au moyen d'états sans perdre d'efficacité

• L'algorithme ne changera pas les performances générales du programme, car il ne fait que ce qui est strictement nécessaire, sans surcoût

Améliorations de performances

• Il est même possible qu'une implantation de ISL conduise à des programmes plus efficaces

• Cela est dû au fait que le programme de changement d'état ne teste jamais l'état courant pour déterminer le traitement à réaliser

• Les callbacks peuvent être installés dynamiquement, et changer pour le même objet dans des états distincts

Callbacks

• ISL gère les callbacks en installant et désinstallant les pointeurs vers les fonctions dynamiquement.

• Contrairement à un programme standard, les callbacks ne commencent jamais par des batteries de tests

Classes vs Etats

• La différence entre classes et états est minime

• L'état d'un objet peut changer, mais pas sa classe

• Cette nuance est due à des raisons techniques, l'implantation des objets reposant sur des concepts comparables aux "struct" C

Annexe diagrammes

d'état de UMLRappels de Notation

Interfaces et Etats

• Les interfaces graphiques, et plus généralement toutes les sortes d'interfaces homme machine sont bien décrites autour de la notion d'état

• On peut laisser son ordinateur allumé pendant des semaines et espérer le retrouver dans le même état en revenant

• Par contre, la prise en compte d'une entrée sera en apparence instantanée dans la plupart des cas

UML

• http://www.sparxsystems.com.au/resources/uml2_tutorial/uml2_statediagram.html

• Object Management Group (OMG) ! http://www.omg.org

• OMG UML Resource Page ! http://www.omg.org/technology/uml

• Object Mentor Inc. - information on UML Design Principles !http://www.objectmentor.com

Etats - Evénements - Transitions

• l'entrée est appelée un événement

• le changement d'état en réponse à un événement est une transition

• l'événement comme le changement d'état sont réputés avoir une durée nulle ou négligeable, mais un état peut durer indéfiniment

Etat composite à deux sous états

Etats élémentaires

Pseudo états

Concurrence

Points d'entrée et de sortie

Automate à Protocole

Pré et post conditions dans les protocoles

Points d'entrée/sortie dans les composites

Jonctions

Décisions

“Self” Transitions

Composite caché

Exemples de régions

Exemple de sous diagramme

Exemple

Variante

Exemple

Invariant d'état

Exemple du téléphone

Exemple

Exemple avec extension

Exercices

• Dessiner le diagramme d’états de l’automate d’un bouton dans une IHM

• Dessiner le diagramme d’états de l’automate d’une application de type « éditeur de texte »

Messages

Evenements déférrés

Extension (2)

Exemple

Exemple

top related