Demarche de conception
7.2.1 Problematique
Les classes formelles ranent les TAG en donnant une representation particuliere mais abstraite. Les operations deviennent des methodes, et les axiomes, s'ils existent sont groupes dans les methodes. Deux approches extr^emes sont possibles pour passer d'un type (abstrait) de donnees a une classe (formelle): la conception plate et la conception ordonnee.
Conceptionplate
En conceptionplate, une seule classe decrit le TAG. Si l'automate est represente par des
predicats d'etat ou une fonction de transition alors les preconditions des methodes sont cal- culables directement par l'algorithme de la section 5.4.3. Dans le cas contraire, le concepteur les exprime en fonction d'une structure abstraite qu'il aura degage. Supposons maintenant une structure abstraite quelconque. Puisque les observateurs peuvent ^etre partiels, certains selec- teurs de champ peuvent ne pas avoir de valeur a un instant donne. Nous utiliserons de preference un champ conditionnel a la valeur indenie (nilou?) usuelle en programmation. Examinons
une conception plate et intuitive du TAGLiftpar la classe formelleFlatLift. FlatLift
inherits from OBJECT
comments: classe formelle de l'ascenseur Lift
param: Weightable
features: limits, bottomLevel, topLevel, level, capacity, weight,up, down, arrived, chgCapacity, getIn, getOut, getOOO, repaired, restart, stop, new, copy, isEqual, describe, install
aspect : lift
eld selectors constraint
bottomLevel : FlatLift ?! Integer
topLevel : FlatLift ?! Integer bottomLevel(Self) < topLevel(Self)
capacity : FlatLift ?! RealGt1 and
contents : FlatLift ?! Set[Weightable] 0 <= weight(Self)
isOnFloor : FlatLift ?! Boolean and
level : FlatLift ?! Integer bottomLevel(Self) <= level(Self) <=
requires: isOnFloor(Self) == true
isOverloaded : FlatLift ?! Boolean topLevel(Self)
requires: isOnFloor(Self) == true
isOnTop : FlatLift ?! Boolean and
requires: isOnFloor(Self) == true
isOnBottom : FlatLift ?! Boolean not(isOnTop(Self) and isOnBottom(Self))
requires: isOnFloor(Self) == true
isGoingUp : FlatLift ?! Boolean and
requires: isOnFloor(Self) == false
distance : FlatLift ?! IntegerGt1 bottomLevel(Self) <= distance(Self) <=
requires: isOnFloor(Self) == false
isStopped : FlatLift ?! Boolean topLevel(Self)
requires: isOnFloor(Self) == false isOutOfOrder : FlatLift ?! Boolean
extensions
;; limits : provides the lift limits (top and bottom levels) limits : FlatLift ?! Tuple(Integer Integer)
limits(Self) == newTuple(bottomLevel(Self), topLevel(Self)) ;; chgCapacity : set a new capacity for the lift
chgCapacity : FlatLift RealGt1 ?! FlatLift
var: Xcap : RealGt1
chgCapacity(Self, Xcap) == copy(Self, capacity = Xcap) ;; up : gets up Xd levels
up : FlatLift IntegerGt1 ?! FlatLift
var: Xd : IntegerGt1
isOnTop(Self) == false ==> up(Self, Xd) ==
FlatLift
extensions
;; down : gets down Xd levels
down : FlatLift IntegerGt1 ?! FlatLift
var: Xd : IntegerGt1
isOnBottom(Self) == false ==> down(Self, Xd) ==
copy(Self, isOnFloor = false, distance = Xd, isGoingUp = false) ;; arrived : the lift reaches the required level
arrived : FlatLift ?! FlatLift
isGoingUp(Self) == true ^(level(Self) + distance (Self) = topLevel(Self)) == true ==> arrived(Self) == copy(Self, isOnFloor = true, isOnBottom = false,
isOnTop = true, level = topLevel(Self))
isGoingUp(Self) == true ^(level(Self) + distance (Self) < topLevel(Self)) == true ==> arrived(Self) == copy(Self, isOnFloor = true, isOnBottom = false,
isOnTop = false, level = level(Self) + distance (Self))
isGoingUp(Self) == false ^(level(Self) - distance (Self) = bottomLevel(Self)) == true ==> arrived(Self) == copy(Self, isOnFloor = true, isOnBottom = true,
isOnTop = false, level = bottomLevel(Self))
isGoingUp(Self) == false ^(level(Self) - distance (Self) > bottomLevel(Self)) == true ==> arrived(Self) == copy(Self, isOnFloor = true, isOnBottom = false,
isOnTop = false, level = level(Self) - distance (Self)) ;; weight : provides the total weight the lift
weight : FlatLift ?! Real
weight(Self) == sum(contents(Self),weight) ;; getIn : a weightable gets inside the lift
getIn : FlatLift Weightable ?! FlatLift
var: Xw : Weightable
isOverloaded(Self) == false ^(weight(Self) + weight(Xw) <= capacity(Self)) == true ==> getIn(Self, Xw) == copy(Self, contents = contents + fweight(Xw)g)
isOverloaded(Self) == false ^(weight(Self) + weight(Xw) > capacity(Self)) == true ==>
getIn(Self, Xw) == copy(Self, contents = contents + fweight(Xw)g, isOverloaded = true) ;; isIn : indicates if the lift contains a weightable
isIn : FlatLift Weightable ?! Boolean
var: Xw : Weightable
isIn(Self,Xw) == belongsTo(contents(Self),Xw) ;; getOut : a weightable gets outside the cage
getOut : FlatLift Weightable ?! FlatLift
var: Xw : Weightable
isOverloaded(Self) == false ^isIn(Self,Xw) == true ==>
getOut(Self, Xw) == copy(Self, contents = contents - fXwg) isOverloaded(Self) == true ^isIn(Self,Xw) == true ^
(weight(Self) - weight(Xw) > capacity(Self)) == true ==> getOut(Self, Xw) == copy(Self, contents = contents - fXwg) isOverloaded(Self) == true ^isIn(Self,Xw) == true ^
(weight(Self) - weight(Xw) <= capacity(Self)) == true ==>
getOut(Self, Xw) == copy(Self, contents = contents - fXwg, isOverloaded = false) ;; stop : stops the lift
stop : FlatLift ?! FlatLift
isStopped(Self) == false ==> stop(Self) == copy(Self, isStopped = true) ;; restart : restarts the lift
restart : FlatLift ?! FlatLift
isStopped(Self) == true ==> restart(Self) == copy(Self, isStopped = false) ;; getOOO : the lift becomes out of order
getOOO : FlatLift ?! FlatLift
isOutOfOrder(Self) == false ==> getOOO(Self) == copy(Self, isOutOfOrder = true) ;; repaired : the lift is being repaired
repaired : FlatLift ?! FlatLift
isOutOfOrder(Self) == true ==> repaired(Self) ==
install(FlatLift, bottomLevel(Self), topLevel(Self), capacity(Self))
class methods
;; install : build an initial lift
install : Integer Integer RealGt1 ?! FlatLift
var: Xbot : Integer,Xtop: Integer,Xcap: RealGt1
install(Xbot, Xtop, Xcap) == newFlatLift(bottomLevel = Xbot, topLevel = Xtop, capacity = Xcap)
La conception d'une telle classe n'est pas triviale a partir du TAG. Les inconvenients de cette approche sont: descriptions souvent operationnelles, preconditions complexes, classes vo- lumineuses pas facile a lire, a comprendre et a specialiser.
Conceptionordonnee
En conceptionordonnee, un TAG est deni par un schema. Rappelons qu'un schema est un
sous-ensemble du graphe d'heritage de racine unique une classe abstraite representant le type de donnees. Par defaut, le schema est initial , c'est-a-dire qu'il comporte deux niveaux: une racine et des feuilles appelees classes terminales. La racine est une classe abstraite correspondant au TAG. Les classes terminales representent les etats (le nom de la classe est la concatenation du nom du type et du nom de l'etat). Voici le schema initial du TAGLift.
MT BT MMU MMD BM TM MB TB BL ML TL BO MO TO
S2 S3 S4 S5 S6 S7 S8 S1
OOO
Lift
Figure 55 : Schema initial pour l'ascenseurLift
Cette representation est induite par la semantique algebrique des etats: un etat est un sous- type (une algebre) du type d'inter^et. Les selecteurs de champ sont des fonctions totales et les preconditions sont plus simples. Le partitionnement facilite la comprehension et la reutilisation mais risque d'entra^ner une explosion combinatoire du nombre de classes avec duplication de methodes identiques (celles des operations applicables a plusieurs etats) : pas de partage de code tant qu'on n'introduit pas des classes abstraites intermediaires. Un autre inconvenient im- portant est la mediocre representation en termes d'objet qui conduit a de l'heritage dynamique, c'est-a-dire qu'un objet est instance de dierentes classes au cours de son existence.
7.2.2 Strategie de conception
Nous souhaitons une approche plus ne que la conception plate et plus ecace que la conception ordonnee ci-dessus. Cette approche intermediaire est resumee dans la gure suivante.
joindre les sous-classes d’une super- classe commune aux classes abstraites étendre la hiérarchie classCB classCC state5 ... TAG abstrait
état 1 état 2 état 3 état 4 ... état n
classeA classeB classeC
AA A1 CB CC
état1 état2 état3 état4 état5 ...
schéma initial d’héritage
grouper les états fortement couplés
schéma structuré
schéma restructuré
Classe abstraite TAG
Classe abstraite TAG
classeA classeB classeC
restructuration construction
initial du schéma
L'idee est de partir du schema initial, de le factoriser puis de le structurer an de mettre en evidence les comportements communs dans des classes abstraites intermediaires et enn de factoriser les sous-classes d'une super-classe pour optimiser la representation. Les classes terminales sont regroupees soit parce qu'elles ont un comportement tres proche (reduction ver- ticale) soit parce que les etats dont elles sont issues sont fortement lies (reduction horizontale). Nous suivons en cela les principes habituels de la conception modulaire: reduction du couplage modulaire, renforcement de la coherence modulaire.
Cette approche est adaptee aux TAG complexes tels que l'ascenseur de la gure 79. Par restructuration successive, le schema est parfois reduit a une seule classe. Ce qui constitue une demarche rigoureuse pour atteindre la conception plate.
Nous proposons donc une methode de ranement des TAG en classes formelles en trois etapes principales: (1) extraire un schema d'heritage a partir du graphe de comportement dynamique, (2) restructurer et regrouper les classes formelles, (3) ecrire les extensions.
USE et CREATE schémas restructuration des axiomes écriture construction du schéma TAG cation TAG spécifi- réduit TAG générateur initial des CF complet des CF schéma schéma Sélection d’un ensemble d’opé-
rations primitives éclatement/union des profils détermination d’une structure abstraite restructuration des classes et des schémas écriture des méthodes secondaires implantation l’automate Réduction de structuré
Figure 57 : Cycle de ranement Les sections suivantes decrivent les trois etapes de la demarche.