Vous êtes sur la page 1sur 184

2011-2012

Cours
d’ATELIER DE
GENIE
LOGICIEL
Donné par Mr WENZI Paul aux Etudiants de G3
Sciences Informatique et L1 Ingenierie de
Système d’information de l’Université
Protestante de Lubumbashi(UPL)

ALL
Gécamines
2011-2012
Avertissements, Bibliographie et Plan de cours
Le nom d’origine de ce Cours est Atelier de Génie Logiciel qui englobait les notions de base de
Gestion des Projets Informatiques (180 H).

The Mythical Man-Month : F. BROOKS – Addison Wesley – 1975


The Software Engineering : Jensen TONIES – Prentice Hall – 1980
Principles of Programming Design and System Development : Michael JACKSON – Prentice Hall

Chap I : Introduction
A. Objet ………………………………………………………………………….. 2
B. But ……………………………………………………………………………... 4
C. Gros et Petits Projets …………………………………………………………... 5
Chap II : Méthodologies
A. Définition ………………………………………………………………………. 6
B. Concepts ……………………………………………………………………….... 6
B1. Concepts au niveau du code ………………………………………………… 6
B2. Concepts au niveau du module …………………………………………… 24
B3. Concepts au niveau de l’architecture logicielle …………………………….. 33
C. Modélisation du cycle de vie ……………………………………………………. 34
D. Grandes tendances actuelles ……………………………………………………. 43
1. Développement Fonctionnel …………………………………………………. 48
2. Développement Composé ……………………………………………………. 49
3. Développement Structurel ……………………………………………………. 50
4. Développement Démonstratif …………………………………………..……. 53
5. Développement Orienté Objet …………………………………………………. 54
E. Ateliers, Travaux Pratiques et Exercices ………………………………………… 127
Chap III : Spécifications
A. Définition et Rappel …………………………………………………………… 152
B. Spécification informelle ………………………………………………………... 152
C. Spécification concrète …………………………………………………………… 152
D. Anomalies possibles ………………………………………..…………………... 157
Chap IV : Etapes Fondamentales de Développement
A. Structuration hiérarchique ………………………………………………………. 160
B. Structuration modulaire ……………………………………….………………... 163
C. Développement de module ……………………………………………………… 167
D. Conception de l’architecture physique …..…………………..……………….... 170
E. Validation ……………………………………………………………………….. 173
E1. Introduction ………………………………………………………………… 173
E2. Test …….…………………………………………………………………. 173
E3. Démonstration ……………………………………………..……………….. 185
Chap V : Ateliers et Notions approfondies
A. Ateliers ………….. …………………………………………………………… 299
B. Notions approfondies …..……………………………………………………... 300

2
CHAP. I : INTRODUCTION
A. OBJET

Rappelons que l’analyse fonctionnelle (composée de l’Etude d’opportunité et de l’Analyse


conceptuelle) a fourni une première définition (± grossière) :
- des données à traiter,
- des traitements, et
- de leurs contraintes.
Elle a éventuellement fourni une maquette.

3
La naissance d’un système d’information (SI) automatisé suit, à un certain niveau d’abstraction,
la forme suivante :

Objectifs, besoins
et contraintes de
l’utilisateur

Analyse d’Opportunité

Solution Globale

Analyse Conceptuelle

Solution conceptuelle

Analyse Logique

Solution Logique

Analyse d’Implémentation

Solution Implémentable

Validation (tests et démonstration)

Solution Exécutable

Maintenance Déclaration d’obsolescence

4
L’objectif du Génie Logiciel est de pouvoir maîtriser par une méthodologie adaptée et
systématique de développement des logiciels, l’ensemble des étapes qui suivent l’analyse
fonctionnelle.
Le génie logiciel ne s’occupe que des problèmes automatisables.
Un problème ou toute autre fonctionnalité est automatisable s’il a été analysé (càd que les objets,
entités, structures, …. de son environnement ont été parfaitement identifiés) et si :
- l’on peut parfaitement définir ses données à l’entrée et ses résultats en sortie,
- l’on peut décomposer le passage de ces données vers ces résultats en une suite d’opérations
élémentaires dont chacune peut être prise en charge par un automate.

Il s’agit donc, à partir du problème posé, :


- d’expliciter clairement les données requises en précisant essentiellement leur nature, leur
domaine de variation et leurs propriétés,
- d’expliciter clairement les résultats attendus en précisant logiquement et rationnellement leurs
relations avec les données requises,
- de décomposer ce problème en sous-problèmes, de manière efficace à l’aide d’une ou de
plusieurs démarches méthodologiques.

B. BUT

Le but du Génie Logiciel est de :


- concevoir une architecture logicielle correcte de la solution,
- concevoir correctement les différents composants (paquettages) de cette architecture,
- coder et de valider les algorithmes et programmes issus de ces composants,
- maintenir méthodiquement ces programmes.

La conception d’une architecture logicielle exige :

1. l’identification de tous les composants de base du système -


L’examen des aspects et de la structure des résultats attendus d’un problème peut permettre
de guider sa décomposition.
La 1ère subdivision d’un problème peut efficacement se faire sur base de la notion
d’Application.
On sait, par définition, qu’une application contient des composants, qui au point de vue
métiers d’utilisateur, ne peuvent que fonctionner très solidairement ensemble.
Par conséquent, deux applications ne peuvent communiquer que par des agrégats (messages
d’avertissement ou d’alerte) et généralement de manière non permanente.
Les applications d’un problème peuvent, dans certaine mesure, former les composants de base
d’un système,

2. l’organisation de ces composants de base -

3. la spécification de chaque composant de base -


Une spécification est une description d’un problème, d’un composant ou de toute autre
fonctionnalité en termes d’objectif à atteindre et de finalité (le «quoi» fonctionnel) et non en
termes de moyens pour atteindre cet objectif ou cette finalité.

5
La conception de chaque composant de l’architecture logicielle exige :

1. le choix de structures des données adéquates,


2. la conception systématique des algorithmes qui réalisent les différents traitements.

Cfr Exercice 40 (Zabulon)

C. GROS ET PETITS PROJETS

Les méthodes de base du Génie Logiciel s’adressent d’abord et surtout à la construction des
systèmes logiciels complexes (gros projets).

On reconnait un gros projet, essentiellement par :


- l’envergure de sa mise en œuvre sur l’organisation (nombre et importance d’unités
fonctionnelles et de fonctionnalités, …),
- l’importance de la consommation des ressources (matérielles, humaines, financières, ….) en
temps et en espace.

Techniquement, les différences essentielles entre petits et gros projets sont reprises dans le
tableau suivant :

En ce qui concerne PETITS PROJETS GROS PROJETS

Les utilisateurs réalisateurs du projet public très large et diversifié

Les mainteneurs réalisateurs du projet autres que les réalisateurs

Les spécifications ± inutiles vitales et indispensables

Les erreurs de programmation de spécification et de programmation

La documentation minimale indispensable

La validation très minime (tests) étendue (tests + démonstration)

L’information centralisée (au sein d’une répartie


(know-how) personne)

Les mises-à-jour impact nul ou faible impact très élevé

6
CHAP. II : METHODOLOGIES

A. DEFINITION

Une méthodologie de développement de logiciels (MDL) est un ensemble des méthodes pour
construire des logiciels selon un processus industriel et rationnel de production.
Elle doit donc s’intéresser aux problèmes de conception, de codage, de validation et de
maintenance de l’architecture logicielle et de ses composants.

A cause de la variété quasi-infinie des problèmes à traiter et des logiciels à développer, il existe
plusieurs MDL.
Par conséquent, aucune MDL ne peut prétendre être adaptée à tous les cas possibles.

L’importance des approches méthodologiques dans la production des logiciels a commencé à


s’avérer nécessaire voire vitale suite à la crise de l’industrie du logiciel vers les années 1970,
par laquelle il a été constaté :
- une augmentation vertigineuse des coûts de production et de conception,
- une non-fiabilité criante des logiciels produits,
- un non-respect des spécifications (explosion d’Ariane 5 – erreur dans un composant logiciel qui
n’était pas indispensable pendant le vol -,
retardement de 2 jours du lancement de la navette spatiale
Columbia – problème dans un logiciel de contrôlel).

Une MDL utilise des modèles et modélise les problèmes selon ses concepts et ses niveaux
d’abstraction. Il n’existe pas de modèle correct unique, mais un modèle est plus ou moins adéquat
selon qu’il permet de capturer certains aspects importants du réel perçu (en négligeant d’autres)
en fonction du but cherché.

B. CONCEPTS

Nous disons que les conséquences majeures d’utilisation des MDL sont :
- une baisse et une maîtrise des coûts de production et de conception,
- une grande fiabilité des logiciels produits,
- le respect des spécifications et des délais.
Pour arriver à cela, une MDL doit baliser son processus de développement avec des concepts
méthodologiques qui peuvent de servir de guide.
Ces concepts sont subdivisés en 3 groupes :
- concepts au niveau de code,
- concepts au niveau de module,
- concepts au niveau d’architecture logicielle.

B1. CONCEPTS AU NIVEAU DE CODE

Un code est tout programme écrit dans n’importe quel langage de programmation.
Il y a 2 concepts principaux à ce niveau : la communicabilité et l’abstraction.

7
B11. Communicabilité

B111. Code Communicable

Un programme doit communiquer avec les automates mais aussi et surtout, à


travers son algorithmique, avec les hommes, car il est destiné à être LU,
COMPRIS, VERIFIE et MODIFIE par eux.
Les commentaires et explications glissés dans un code sont importants mais
insuffisants.
Un programme est communicable si et seulement si il peut dévoiler sa structure
aux hommes.
Un code communicable est un code bien conçu et qui est par conséquent
facilement maintenable.
La structure d’un code se dévoile au travers de l’algorithmique et du contexte de
la solution apportée au problème traité (cfr chapitre III).

B112. Algorithme et Décomposition

L’algorithme est l’outil de base de production d’un code et de communication


entre les diverses personnes (concepteurs, programmeurs, analystes, …)
impliquées dans un problème.
Un algorithme permet de décrire l’ensemble des actions qui doivent être réalisées
en vue de satisfaire une spécification donnée (cfr chapitre III).

Un algorithme suppose l’existence d’un processeur (abstrait ou réel) capable de


comprendre et d’exécuter les actions de cet algorithme.
Généralement, le développement d’un problème donné, ne fournit pas qu’un seul
algorithme, mais une suite d’algorithmes successifs qui s’adressent à une suite de
processeurs abstraits de moins en moins compétents.

Cette suite d’algorithmes successifs est le résultat d’une décomposition itérative,


en fonction de certains critères du problème, des actions à réaliser en actions
de plus en plus simples (moins complexes) :
- le premier algorithme se limite ainsi à définir la spécification informelle et
s’adresse à un processeur abstrait très compétent,
- le dernier algorithme ne contiendra plus que des actions suffisamment simples
pour être directement exécutables par un processeur réel.

La production efficace d’algorithme exige donc un processus de décomposition.

Une décomposition est correcte si l’ensemble des sous-actions résultant de cette


décomposition réalise effectivement la spécification de l’action qui a été
décomposée,
autrement dit, si S est la spécification de l’action A, alors la décomposition de
l’action A en sous-actions A1, A2, … ,An confère respectivement à chacune de ces
sous-actions des sous-spécifications S1, S2, … ,Sn de S telles que
S = S1 U S2 U … U Sn.

La correction de l’ensemble de décompositions entraîne, par induction, la


correction de l’action qui a été décomposée.

8
Le dernier algorithme dans le développement d’un problème est le moins abstrait
et doit permettre une codification automatique des programmes nécessaires en 1
ou plusieurs langages de programmation exécutables par des automates réels.

Ce dernier algorithme se réfère via ses instructions, de manière plus ou moins


précise, aux objets de base du problème qui ont été spécifiés ou suggérés par
l’analyse fonctionnelle.

Les principaux objets de base sont :


- le CONTENANT : qui est toute cellule de mémoire (adresse de mémoire)
capable de garder des informations,
- l’INFORMATION : qui représente l’état d’un contenant,
- l’ATOME : ensemble de tous les états possibles d’un contenant donné,
- l’IDENTIFICATEUR : qui représente un ensemble de contenants.

L’espace d’exécution d’une instruction, particulièrement au sein du dernier


algorithme est le n-uplet de n objets de base ayant au moins une occurrence dans
cette instruction.
Ex : l’instruction a ← (b + c) * a + b a comme espace d’exécution (a,b,c),
l’instruction a ← b + 4 a comme espace d’exécution (a,b).

Ces objets de base permettent de définir un certain nombre de fonctions de base,


selon le schéma particulier suivant :

IDENTIFICATEUR

résolution valeur sémantique

CONTENANT ATOME INFORMATION


état contenu

Par exemple, «résolution» est la fonction qui transforme un identificateur en son


adresse.

L’espace d’exécution et les fonctions de base sont des notions très importantes
au sein du processus de démonstrations de programmes.
Elles permettent, entre autres, de fournir des traces formelles et numériques
associées aux algorithmes.
La trace formelle est un outil qui permet de procéder à une exécution formelle
d’un algorithme sur des valeurs symboliques (algébriques).
La trace numérique regroupe un ensemble d’outils qui permettent de procéder à
des vérifications d’un algorithme lors de son exécution par un automate
(tests).

9
B113. Mécanismes de Contrôle

Le processus de la décomposition d’une action peut être réalisé à l’aide de


plusieurs mécanismes de contrôle de flux.
Pour exprimer efficacement l’algorithmique utilisée, un code devrait se limiter à
une utilisation judicieuse et bien coordonnée des 3 mécanismes de contrôle de
flux suivants : la séquence, l’alternative et l’itération.
C’est l’utilisation judicieuse et bien coordonnée des 3 mécanismes de contrôle
de flux au sein d’une algorithmique qui dévoile la structure du code
correspondant et qui en simplifie la compréhension et la maintenance.

On peut remarquer, en passant, que ces 3 mécanismes sont aussi, à un certain


niveau d’abstraction, les seuls qui gouvernent le monde et la nature.

L’itération implémente toujours une situation de récurrence, dans laquelle des


actions semblables A doivent être répétées un certain nombre de fois sur des
données au sein d’une boucle de traitement.
L’itération passe ainsi par des situations intermédiaires S1, S2, … ,Sn pour
atteindre, à la fin de la récurrence, l’objectif désiré par l’itération.

Pour spécifier l’itération, il faut :


1. identifier une variable i qui permettra de numéroter les Si
(généralement c’est l’indice de la boucle de traitement, pour une itération
simple),
2. décrire les Si en fonction de cette variable i, cad il faut que
Si = S(i).
Si la situation initiale S0 (avant d’entrer dans la boucle), peut aussi se mettre,
au prix d’une transformation simple sous la forme S(i), avec i = 0,
alors la suite A peut être totalement défini par le fait qu’elle permet de passer
de la situation S(i) à la situation S(i+1) pour tout i : 1 ≤ i < n.
S est appelé situation invariante de l’itération et
S dotée de toutes les autres assertions nécessaires est appelé invariant (ou
hypothèse de récurrence).
Il faut noter que les situations intermédiaires, la situation invariante et
l’ invariant sont des assertions ou des prédicats.

10
Exemple : Soit l’algorithme : début
i←1
tant que i ≤ 10 faire a[i] ← 0
i←i+1
fin
Trouvez sa spécification, les situations intermédiaires, la situation
invariante et l’invariant de sa boucle.

Spécification : mise à zéro des 10 (premiers) éléments du tableau a,


Situations intermédiaires :
S1 = {a[1]=0}
S2 = {a[1]=0 Λ a[2]=0}
S3 = {a[1]=0 Λ a[2]=0 Λ a[3]=0}
.
.
.

S10 = {a[1]=0 Λ a[2]=0 Λ a[3]=0 Λ ... Λ a[9]=0 Λ a[10]=0}


Situation invariante : S(i) = {a[j]=0, pout tout j : 1 ≤ j ≤ i },
On peut voir que cette expression permet de
générer n’importe quelle situation intermédiaire
Si.
En plus, elle est aussi correcte pour S0 car
S(0) est vraie, vu que l’intervalle 1 ≤ j ≤ i est
Vide.
Invariant = {a[j]=0, pout tout j : 1 ≤ j ≤ i, i € N, j € N, a[j] € N},
par exemple.

11
B114. Notions fondamentales de base sur les codes

Les notions fondamentales sur les matériaux de base de code permettront


finalement de démontrer qu’un programme P est cohérent par rapport à ses
spécifications concrètes (cfr chapitre III).
Si Q représente la pré-condition de P et si R représente sa post-condition, il
faudra établir {Q} P {R},
ce qui signifie : «si le programme P se termine, alors toute exécution de P, par un
processeur réel ou abstrait, à partir d’un état initial qui satisfait
l’assertion Q produira un état final qui satisfait l’assertion R».
Le programme P est en fait une suite d’instructions S1,S2,S3,…,Si, …, Sm.
Chaque Si peut être une instruction d’affectation, une instruction de sélection,
une instruction d’itération ou un bloc d’instructions (séquence d’instructions).

Il s’agira donc finalement d’établir que l’on a :

{pré-condition de P}
S1
{ass1}
S2
{ass2}
S3
{ass3}
.
.
.
Sm-1
{assm-1}
Sm
{post-condition de P}

où assi est une assertion intermédiaire qui forme la post-condition de


l’instruction Si et la pré-condition de l’instruction Si+1, et qui caractérise un
état intermédiaire du programme P.
Les différentes assi peuvent s’obtenir les unes à partir des autres au moyen des
transformations systématiques qui procèdent de la démarche de la
configuration descendante (évaluation progressive) ou de la
configuration ascendante (substitution régressive).

Par la configuration descendante, il faut :


- calculer la façon dont l’instruction Si transforme l’assertion assi-1 pour
produire l’assertion assi,
- vérifier que l’assertion assm correspond bien à R.
Les expressions de la configuration descendante sont préfixées par ‘+’ ou par
‘sp’ (Strongest Post-condition).

12
Par la configuration ascendante, il faut :
- calculer la façon dont l’instruction Si transforme l’assertion assi pour
produire l’assertion assi-1,
- vérifier que l’assertion ass0 correspond bien à Q.
Les expressions de la configuration ascendante sont préfixées par ‘-’ ou par ‘wp’
(Weakest Pré-condition).

a. Règles de calcul pour l’affectation

Si R est une post-condition de l’instruction d’affectation «x ← E» (x est une


variable et E est une expression quelconque) et Q sa pré-condition, alors :

1. en configuration ascendante, on a :

l’assertion R étant connue et


x←E
l’assertion Q non connue,

d’où la règle : Q ≡ - (x ← E, R) = R [x/E]

R [x/E] indique l’assertion R dans laquelle toutes les occurrences libres de


la variable x ont été remplacées par l’expression E,

2. en configuration descendante, on a :

l’assertion Q étant connue et


x←E
l’assertion R non connue,

d’où la règle : R ≡ + (x ← E, Q) = Q [x/x0] Λ x = E [x/x0]

Q [x/x0] indique l’assertion Q dans laquelle toutes les occurrences libres de


la variable x ont été remplacées par la variable symbolique x0.
E [x/x0] indique l’expression E dans laquelle toutes les occurrences libres
de la variable x ont été remplacées par la variable symbolique x0.

13
Exemples :

Soit :
Q=?

x←x+1

R = (x > 4)

Q ≡ - (x ← x + 1, x > 4)  x + 1 > 4  x > 3

Soit :
Q = (y ≥ -1)

y←y-a-1

R=?

R ≡ + (y ← y - a - 1, y ≥ -1) = (y0 ≥ -1) Λ (y = y0 - a - 1) 

(y0 ≥ -1) Λ (y0 = y + a + 1) 

y + a + 1 ≥ -1  y + a ≥ -2

14
b. Règles de calcul pour la séquence (bloc d’instructions)

Si R est une post-condition de la séquence d’instructions «S1;S2;S3»


et Q une pré-condition de cette même séquence d’instructions, alors :

1. en configuration ascendante, on a :

S1

S2 l’assertion R étant connue et


l’assertion Q non connue,

S3

d’où la règle : Q ≡ - (S1;S2;S3, R) = - (S1, -(S2, -(S3, R)))

Cette règle génère en fait 2 règles intermédiaires T et V telles que :


T = - (S3, R) et V = - (S2, T)
de manière telle que - (S1, V) fournisse Q

2. en configuration descendante, on a :

S1

S2 l’assertion Q étant connue et


l’assertion R non connue,

S3

d’où la règle : R ≡ + (S1;S2;S3, Q) = + (S3, +(S2, +(S1, Q)))

Cette règle génère en fait 2 règles intermédiaires T et V telles que :


T = + (S1, Q) et V = + (S2, T)
de manière telle que + (S3, V) fournisse R

15
Exemples :

Dans l’algorithme suivant, si R = (i = 5) Λ (j = 9), trouvons Q :

i←i+j
V

j←i-j
T

i←i-j

R = (i = 5) Λ (j = 9)

Q ≡ - (i ← i + j; j ← i - j; i ← i - j, R) =
- (i ← i + j, -( j ← i - j, -( i ← i - j , R)))

T = (i – j = 5 Λ j = 9)  (i = 5 + j Λ j = 9)
 (i = 14 Λ j = 9)

V = (i = 14 Λ i – j = 9)  (i = 14 Λ – j = 9 - i)
 (i = 14 Λ j = 5)

Q = (i + j = 14 Λ j = 5)  (i = 9 Λ j = 5)

16
Dans l’algorithme suivant, trouvons R :

Q = (i = i0 Λ j = j0)

i←i+j
V

j←i-j
T

i←i-j

R=

V ≡ (i = i0 Λ j = j0) Λ (i = i0 + j0),
dans i ← i + j, il s’agit d’abord de remplacer, dans le membre de droite,
i par sa valeur i0 et j par sa valeur j0 et ensuite de remplacer dans
l’expression obtenue toute occurrence i par i0.
On doit finalement oublier l’ancienne valeur de i = i0 (à cause de
l’instruction d’affectation qui a écrasé l’ancienne valeur de i) :
 (j = j0 Λ i = i0 + j0)

T ≡ (j = j0 Λ i = i0 + j0) Λ (j = i0 + j0 - j0)
 (i = i0 + j0 Λ j = i0)

R ≡ (i = i0 + j0 Λ j = i0) Λ (i = i0 + j0 - i0)
 (j = i0) Λ (i = j0)

17
c. Règles de calcul pour la sélection

Si R est une post-condition de l’instruction de sélection


«si B alors S1 sinon S2»
(où B dénote une expression booléenne, et S1 et S2 des instructions)
et Q une pré-condition de cette même instruction de sélection, alors :

1. en configuration ascendante, on a :

S1 S2

d’où la règle Q ≡ - (si B alors S1 sinon S2, R) =


(B Λ – (S1,R)) V (┐B Λ – (S2,R))

2. en configuration descendante, on a :

S1 S2

d’où la règle R ≡ + (si B alors S1 sinon S2, Q) =


+ (S1,B Λ Q) V + (S2,┐B Λ Q)

18
d. Règles de calcul pour l’itération

Si R est une post-condition de l’instruction d’itération


«tant que B faire S1»
(où B dénote une expression booléenne, et S1 une instruction)
et Q une pré-condition de cette même instruction d’itération, cad

tant que B faire S1

alors, étant donné que l’invariant I de l’itération reste toujours vrai, quelle que
soit la configuration, il faudra établir :

1. Q  I
2. I Λ B  I
3. I Λ non B  R

Remarque : La trace formelle est finalement un outil efficace d’aide à la


compréhension des algorithmes qui utilise massivement la notion de
«raccourci» sur les valeurs symboliques, essentiellement lors des
modifications des valeurs de variables au sein de la configuration
descendante.

19
e. Exemples et Exercices

Trouvez, par la trace formelle, des pré-conditions et post-conditions


de toutes les instructions des algorithmes suivants :

début
i=i0
lire a
i=i0 Λ a=a0
lire b
i=i0 Λ a=a0 Λ b=b0
lire c
i=i0 Λ a=a0 Λ b=b0 Λ c=c0
d ← a+b+c+i
i=i0 Λ a=a0 Λ b=b0 Λ c=c0 Λ
fin d=a0 + b0 + c0 + i0

début
b=b0
écrire b
b=b0
a←b
a=b0 Λ b=b0
i←7
i=i0 Λ a=b0 Λ b=b0
lire c
i=i0 Λ a=b0 Λ b=b0 Λ c=c0
v ← (i + a) * c
i=i0 Λ a=b0 Λ b=b0 Λ c=c0 Λ
fin v=(i0 + b0) * c0

début
vrai car pas d’assertion
a ← -7
a=a0
i←8
a=a0 Λ i=i0
b←4
a=a0 Λ i=i0 Λ b=b0
si i < a alors y ← i - b
y=i0-b0 Λ i0 < a0 Λ a=a0 Λ i=i0 Λ
sinon y ← i + b b=b0
y=i0+b0 Λ i0 ≥ a0 Λ a=a0 Λ i=i0 Λ
fin b=b0

20
B12. Abstraction

B121. Généralités

Le concept d’abstraction consiste à construire au sein des langages et de


plateformes de développement des machines abstraites de plus en plus
puissantes jusqu’à ce que l’utilisateur, à la limite, n’ait plus besoin que d’une
seule instruction pour exprimer une action ou une spécification.

Une machine abstraite cache des spécificités d’une machine réelle (ou d’une
autre machine abstraite) en vue d’augmenter la portabilité et la facilité de
développement, d’utilisation et de maintenance des programmes.

C’est ce concept qui est à l’origine de la création des langages de haut niveau et
de l’amélioration de l’environnement et de la productivité des programmeurs
essentiellement, en offrant un espace de raisonnement, de développement et de
démonstration moins encombré.

Ce concept permet finalement de définir un objet quelconque en termes d’actions


que l’on peut y effectuer et non en termes de son implémentation.

B122. Notions de base (sur des contenants)

Pour toute construction d’abstraction qui a comme fondement les contenants


d’un domaine, il est toujours nécessaire de les catégoriser afin de déterminer,
parmi ces contenants, des familles Fi bien distinctes, telles que :
- il existe k tel que Fk ≠ vide,
- ¥ i, ¥ j, i ≠ j => Fi ∩ Fj = vide.

Une classification courante se base sur la signature possible de ces contenants et


génère des familles qui contiennent des symboles fonctionnels i-aires.
Notons F = U Fi .
Si F* représente l’ensemble des expressions sur F,
alors le couple (F*,F) constitue une algèbre abstraite qui fournit des schémas
fonctionnels, c-à-d que pour tout symbole fonctionnel f € F, on définit ainsi
une application z : F*n F* qui fournissant ces schémas fonctionnels.

Une algèbre formelle informatique (AFI) est une algèbre abstraite dans laquelle
toutes les Fi sont non vides.

De manière générale, si l’on dispose d’un ensemble X (non vide) de symboles de


variables qui est muni d’un ensemble Ώ d’opérations possibles sur X, on a une
AFI qui permet de construire une fonction ψ telle que ψ : (F*, F) X et qui est
appelée fonction d’interprétation de l’AFI.
X est alors qualifié de l’univers de cette AFI.
On peut construire plusieurs fonctions d’interprétation ψ sur un ensemble donné
de familles de symboles.

21
L’AFI, telle que exposée, càd dotée d’une fonction d’interprétation, constitue
un MODELE ABSTRAIT pour tout un ensemble des familles d’éléments
fonctionnels, car il suffit de changer le modèle d’interprétation pour implanter
une structure de données spécifiques.

Exemple 1 :
Considérons les ensembles suivants :
F0 = { a, b, c} : constantes
F1 = { h, k} : symboles unaires (1-aires)
F2 = {g} : symboles binaires (2-aires)
F3 = {f} : symboles ternaires (3-aires).
Soit le schéma fonctionnel fghafakbcchkgab (construite avec l’opérateur de
concaténation), déduisons-en une expression parenthésée et une expression
arborescente :

=> expression parenthésée :


f (g (h (a), f (a, k (b), c) ), c, h (k (g (a, b) ) ) )

=> expression arborescente :

g c h

h f k

a a k c g

b a b

22
Exemple 2 :
Considérons les ensembles suivants :
F0 = { x, y} : constantes
F2 = {f, g} : symboles binaires (2-aires).
F est donc F0 U F2.

Si l’AFI est composée d’éléments suivants :


- univers X = R
- opérateurs Ώ = { +, * }
- interprétation ψ. (F*, F) (R, Ώ) définie comme suit :
ψ (f) : R2 R : (a,b) ψ (f) (a,b) = a + b
ψ (g) : R2 R : (a,b) ψ (g) (a,b) = a * b
ψ (x) = a0 pour la constante x
ψ (y) = a1 pour la constante y.
Trouvons l’interprétation du schéma fonctionnel fxgyx.

=> L’expression parenthèse est f (x, g (y, x)),


L’interprétation est ψ (fxgyx) et qui est égal à
ψ (f (x, g (y,x)))
= ψ (f) (ψ (x), ψ (g) ( (ψ (y), ψ (x) ))
= (ψ (x) + ψ (g) (ψ (y), ψ (x) ) : en appliquant les propriétés de ψ (f)
= ψ (x) + ψ (y) * ψ (x) : en appliquant les propriétés de ψ (g)
= a0 + a1 * a0.

Exemple 3 :
Considérons les mêmes ensembles que ceux de l’exemple 2 avec l’AFI :
Univers X = N, Ώ = {reste, ≥} et
ψ est définie comme suit : ψ (g) : N2 N : (a, b) ψ (g) [a, b] = reste (a, b)
ψ (f) : N2 N : (a, b) ψ (f) [a,b] = a ≥ b
ψ (x) = n0 (n0 € N)
ψ (y) = n1 (n1 € N).
Interprétons le même schéma fonctionnel fxgyx.

=> L’interprétation est ψ (fxgyx) et qui est égal à


ψ (f (x, g (y,x)))
= ψ (f) (ψ (x), ψ (g) ( (ψ (y), ψ (x) ))
= (ψ (x) ≥ ψ (g) (ψ (y), ψ (x) ) : en appliquant les propriétés de ψ (f)
= ψ (x) ≥ reste (ψ (y), ψ (x) ) : en appliquant les propriétés de ψ (g)
= n0 ≥ reste (n1 , n0).

23
B123. Généralisation

Parmi les outils qui se servent des notions d’AFI et d’interprétation, signalons le
TAA (Type Abstrait Algébrique) qui permet de spécifier des structures de
données d’une manière très générale sans avoir la nécessité d’en connaître
l’implémentation.

La syntaxe du TAA est définie par l’AFI et l’ensemble X (univers),


la sémantique du TAA est définie par ψ et l’ensemble Ώ et est généralement
exprimée sous forme d’axiomes et de pré-conditions.
Un TAA peut réutiliser d’autres TAA préalablement définis sous forme de
hiérarchie; la signature totale, dans ce cas, est la réunion des signatures de tous
les TAA.

Exemple d’un TAA :


Univers : booléens
Opérations : H : constantes booléennes
F : constantes booléennes
┐ : booléens booléens
V : booléens x booléens booléens
Λ : booléens x booléens booléens
Axiomes : ┐(H) = F,
┐(F) = H,
a Λ H = a,
a Λ F = F,
a V H = H,
a V F = a.

Quand la partie “axiomes” d’un TAA est occultée, on obtient simplement un


Type Abstrait des Données (TAD).

24
B2. CONCEPTS AU NIVEAU DE MODULE

Les concepts majeurs de ce niveau sont : la cohésion interne, le couplage, la complexité,


la correction, la correspondance et la forte capacité de cacher l’information.

B21. Définition

Un problème peut être décomposé en plusieurs modules. La modularisation se réfère à


cette activité, et la modularité, se réfère au produit issu d’une telle décomposition.
Un module correspond donc à un sous-problème et est relatif à une abstraction faite
sur les propres données et structures de ce sous-problème sans référence aux
autres sous-problèmes de la décomposition.

Un module peut donc être vu comme étant constitué d’un ou de plusieurs codes.
Un module correspond finalement à une unité fonctionnelle de composants d’une
architecture qui peut être :
- analysée séparément ;
- utilisée plusieurs fois dans cette même architecture ou dans une autre.

La notion de module est une des notions centrales en Génie Logiciel et le but principal
de la décomposition d’un problème en modules est de réduire sa complexité de façon
rationnelle (cfr modèles de conception).

B22. Conditions

Pour qu’une décomposition modulaire d’un problème (en modules) puisse réduire sa
complexité de façon rationnelle, il faut que :
- chaque module puisse être défini de façon abstraite, c’est-à-dire en se référant à
d’autres abstractions supposées connues dans le domaine du problème à traiter ou
dans d’autres domaines,
- chaque module puisse cacher toute sa complexité interne, c’est-à-dire avoir une forte
capacité de cacher ses traitements et ses données internes (encapsulation),
- les rapports entre les autres modules ou composants de l’architecture (interface) soient
les plus simples possibles, c’est-à-dire que le couplage de module doit être faible le
plus faible possible (faible degré de couplage).

Un module peut être implémenté logiquement ou physiquement, au sein des langages


de programmation sous forme de routine, procédure, fonction, module, cluster,
paquetage, classe ou objet essentiellement.

B23. Sièges des abstractions

Chaque domaine, discipline, science ou art possède ses abstractions (généralement


issues de la nature, des pratiques ou des raisonnements de bon sens).
La modularisation d’un problème donné peut faire référence aux abstractions
disponibles dans d’autres domaines, disciplines, sciences ou arts.

25
Plus grande est la prédisposition d’une discipline à user des notions abstraites,
plus grande est l’influence de cette discipline sur le travail de décomposition des
problèmes.

La science informatique est pourvue de plusieurs grandes abstractions


«naturelles» parmi lesquelles :
- création d’éléments d’un ensemble de lots d’information (apparentés) ou non,
- recherche d’éléments au sein d’un ensemble de lots d’information,
- mise à jour des éléments au sein d’un ensemble de lots d’information,
- lecture d’éléments d’ensembles de lots d’information,
- impression d’éléments d’ensembles de lots d’information,
- copie d’éléments d’ensembles de lots d’information (avec destruction des
éléments de départ ou non),
- définition de contrôles d’accès, de cohérence, de compatibilité,
- traitement cohérent d’éléments de structures stables,
- vérification qu’un contenant contient un nombre déterminé d’occurrences d’un
type donné,
- définition d’interdépendance entre objets de façon telle que, quand un objet subit un
changement d'état, tous ceux qui lui sont assujettis changent aussi automatiquement
leur état,
- définition d’itération au sein des lots d’informations afin de fournir un moyen
d'accès séquentiel, aux éléments de ces lots d’informations sans se soucier des
détails d’implémentation interne de ceux-ci,
- définition d’interface unifiée ou de haut niveau (pour créer des objets ou
pour y accéder),
- conversion d'interfaces,
- définition d’encapsulation afin de diminuer le degré de couplage entre éléments,
- définition d’interprétation (parsing) des phrases d’un lexique,
- conversion de représentation, de codification (modification de forme) ou de valeur
d’éléments,
- génération (aléatoire ou non) d’éléments,
- restauration d’états des objets
- définition et contrôle de sécurité, d’accès, de manipulation d’objets,
- gestion de parallélisme,
- gestion de synchronisation.

B24. Exemples

1. Soit une unité fonctionnelle calculant la factorielle d’un nombre entier.


Cette unité peut devenir un module. Pourquoi ? :
- l’abstraction accomplie par ce module est relative à la notion théorique de la
factorielle (définition mathématique),
- ce module cachera, par conséquent, en son sein, toute sa complexité interne,
- les rapports entre ce module et d’autres composants qui pourraient utiliser les
services de ce module seront automatiquement plus simples.

26
2. Soit une unité fonctionnelle qui trie des nombres réels en ordre croissant.
Cette unité peut devenir un module. Pourquoi ? :
- l’abstraction accomplie par ce module est relative à la notion naturelle
d’Ordre dans R,
- ce module cachera, par conséquent, en son sein, toute sa complexité interne,
- les rapports entre ce module et d’autres composants qui pourraient utiliser les
services de triage de ce module seront automatiquement plus simples.

3. Soit une unité fonctionnelle dont le rôle est, chaque fois qu’on lui transmet 3 réels
a, b, c, de calculer :
- g = racine carrée de a.c, c’est-à-dire la moyenne géométrique g de a et c,
- m = (b+g)/2, c’est-à-dire la moyenne arithmétique m de b et g.
Cette unité ne peut pas devenir un module (dans notre monde). Pourquoi ? :
- deux abstractions seraient accomplies dans ce module :
théorie de la moyenne arithmétique et théorie de la moyenne géométrique,
- ce module cachera, par conséquent, en son sein, toute sa complexité interne
de ces 2 théories,
- les rapports entre ce module et d’autres composants qui pourraient utiliser les
services de triage de ce module risquent d’être complexes et compliqués.

4. Soit une unité fonctionnelle qui, étant donné 5 entiers a, b, c, d, et e renvoie le


résultat a + a + a + b + c + d + e.
Cette unité peut devenir un module. Pourquoi ? :
- l’abstraction accomplie par ce module est relative à la notion d’arithmétique dans R,
- ce module cachera, par conséquent, en son sein, toute sa complexité interne,
- les rapports entre ce module et d’autres composants qui pourraient utiliser les
services de ce module seront automatiquement plus simples.

B25. Aspects de la modularité

La modularité, qui est le processus de la production des modules, cache en fait 5


aspects essentiels suivants :

1. la décomposabilité modulaire : qui permet, en isolant les abstractions du problème,


de pouvoir le décomposer correctement,
efficacement et hiérarchiquement en ses sous
problèmes de manière itérative,
2. la composition modulaire : qui permet de recombiner et de ré-agencer des modules
connus pour répondre à d’autres abstractions complexes,
3. la continuité modulaire : qui permet de réduire l’impact de changements dans les
spécifications à un nombre limité et restreint des modules
d’un problème,
4. la compréhension modulaire : qui permet d’appréhender plus facilement le
fonctionnement des différentes parties de la solution
apportée à un problème sans avoir à en connaître
absolument toute la solution,
5. la protection modulaire : qui permet de limiter les effets des incidents (erreurs) lors
de l’exécution, à un nombre limité et restreint des parties de
la solution apportée à un problème.

27
B26. Cohésion Interne

Elle exprime le type de lien qui existe entre les différents composants internes d’un
module, c’est donc la «colle» qui tient ensemble ces différents composants.
Chacun de ces composants interne doit résoudre une partie du problème qui est posé au
module.
Il est évident qu’un haut niveau de cohésion interne est souhaitable.

Sept niveaux de cohésion interne ont été suggérés :

1. Cohésion accidentelle :
cohésion que l’on obtient lorsque les différents composants d’un module y sont par
pure coïncidence.

2. Cohésion logique :
cohésion qui signifie que les différents composants d’un module accomplissent la
même fonction logique, par référence à d’autres abstractions supposées déjà connues
dans le domaine du problème, mais ils ne travaillent pas ensemble.

Ex : Module composé de 10 types de routines d’impression qui ne travaillent pas


ensemble mais accomplissement seulement la même fonction logique
d’impression.

3. Cohésion temporelle :
cohésion qui signifie que les différents composants d’un module accomplissement
des fonctionnalités liées par le temps.

Ex : un module d’initialisation accomplissant un ensemble d’opérations nécessaires


en début d’exécution d’autres programmes. Ces différentes opérations tiennent
comme un tout et doivent s’accomplir séquentiellement l’une après l’autre.

4. Cohésion procédurale :
cohésion qui est généralement accidentelle et qui est issue des actions des processus
procéduraux (processus «pas à pas» guidés par la seule relation de «précédence» entre
leurs actions).
Par exemple, un processus qui a été conçu sur la seule base purement procédurale,
à l’aide d’un ordinogramme, a beaucoup de chances de fournir des modules qui ont
une cohésion procédurale.

5. Cohésion communicationnelle :
cohésion obtenue lorsque les différents composants du module opèrent sur des données
communes.
Un module qui, au travers de son processus, applique de manière persistante
différentes actions sur un même ensemble de données communes possède une cohésion
communicationnelle.
C’est cette cohésion qui est à la base de mise en œuvre des bases de données.

28
6. Cohésion séquentielle :
cohésion obtenue lorsque le module représente une portion de diagramme de flot de
données, c-à-d qu’un composant A transmet des données à un composant B qui
accepte ces données, les enrichit (transformation, utilisation dans un calcul (cUse), …)
et les transmet à un autre composant C.

7. Cohésion fonctionnelle :
cohésion obtenue lorsque chaque composant du module accomplit une seule fonction
simple.
Un tel module f, par conséquent, transforme une donnée d’input simple X en une
donnée d’output simple Y, c'est-à-dire que Y = f(X).
C’est le plus haut niveau de cohésion interne.
Exemple : calcul de racine carrée (par la méthode de Newton).

Un module peut présenter plusieurs niveaux de cohésion interne.


Un système ou une architecture logicielle peut contenir des modules qui sont pourvus
chacun des niveaux de cohésion interne différents.

Mr Ed. YOURDON (dans «Structured Design» - Editions YOURDON Press –


New York 985), après une étude d’une quinzaine d’années, a démontré
mathématiquement que les 7 niveaux de cohésion interne pouvaient constituer une bonne
base de comparaison des modules et a proposé sur une échelle de 0 à 10, les cotations
suivantes :
cohésion accidentelle : 0, cohésion logique : 1, cohésion temporelle : 3,
cohésion procédurale : 5, cohésion communicationnelle : 7,
cohésion séquentielle : 9, cohésion fonctionnelle : 10.

B27. Couplage

1. Communication et Protocoles

L’encombrement de la communication entre les modules est mesuré par le couplage


des modules et par la complexité de l’interface.
La notion de couplage se réfère à la taille des informations nécessaires pour établir la
communication entre 2 modules et la notion de complexité de l’interface se réfère au
nombre d’informations nécessaires à cette communication.

L’interface établie entre 2 modules détermine le type de protocole de communication,


qui est généralement un protocole qualifié «d’égal à égal» ou de «client-serveur».

Dans le protocole «client-serveur», le module «client» demande au module «serveur»


d’effectuer un service et le module «serveur» est tenu de retourner au module «client»
le résultat du service demandé car la correction (respect de spécification) du module
«client» dépend entièrement de la correction du module «serveur» (cfr Chap. IV).

Dans le protocole «égal à égal», un module A peut s’adresser à un autre module B sans
que l’incorrection (non respect de spécification) du module B n’affecte la correction du
module A. (cfr Chap. IV).
Le protocole «client-serveur» conduit à une décomposition VERTICALE (en couches)

29
du système et le protocole «égal à égal» conduit à une décomposition
HORIZONTALE (en partitions) du système.

2. Communication et Décomposition

Dans une découpe verticale, une couche génère une abstraction de la couche qui se
trouve en dessous et fournit une base d’implémentation pour la couche au-dessus.
Chaque couche est donc «client » de celle qui la précède et «serveur» de celle qui la
suit.
Vu sous cet angle, un système peut être représenté à l’aide de ses 2 couches extrêmes :
- la couche la plus haute qui représente le système lui-même du point de vue de
l’utilisateur,
- la couche la plus basse qui définit les ressources ou les unités de gestion disponibles
(atomiques et simples) en ce qui concerne les matériels, le système d’exploitation, …

Comme en ce qui concerne un problème donné, on dispose généralement de ces 2


couches, le concepteur aura la responsabilité de définir les couches intermédiaires
nécessaires en fonction de l’écart entre ces 2 couches, écart qui exprime, en d’autres
mots, la taille et la complexité de ce problème.

Dans une découpe horizontale, une partition permet de réaliser un ensemble cohérent
des fonctionnalités repérées dans l’analyse fonctionnelle au sein d’un système.
On peut ainsi, par exemple, découper horizontalement un système d’exploitation en
composants contenant les fonctionnalités de :
- gestion de programmes,
- gestion de processus,
- gestion de fichiers,
- gestion de communication de réseau, …
et générer une interface de type «égal à égal» entre ces composants.

Lorsque la décomposition d’un problème ou d’un processus est faite arbitrairement,


on obtient un HAUT DEGRE de couplage, symbolisé par une structure de
MODULARITE MOSAIQUE :

P
A B D E

F I K
J
G H

30
Conséquences immédiates d’une telle modularité :
- Augmentation de la complexité des interfaces et
- multiplication de ces interfaces (car les modules communiquent d’une manière
incontrôlée, indésirable et inutile).

Lorsque la décomposition d’un problème ou d’un processus est faite à l’aide d’une
structure arborescente, on obtient un FAIBLE DEGRE de couplage, symbolisé par une
structure de MODULARITE HIERARCHIQUE :

B C

D E F

G H I J K

Conséquences immédiates d’une telle modularité :


- simplification des interfaces,
- plus grande visibilité sur les abstractions du problème,
- plus grande testabilité,
- plus grande maintenabilité.

Les structures arborescentes les plus utilisées sont :


la HIERARCHISATION, la MODULARISATION LOGIQUE et le
RAFFINEMENT ITERATIF.

B28. Complexité

Ce concept se réfère à l’encombrement des actions fines des composants au sein d’un
module et permet de contrôler la consommation du temps et de l’espace par ce module.
Il est fonction de la taille du problème traité et des structures de données sous-jacentes
lorsque cette taille croit indéfiniment.
La complexité d’un module est donc fonction de la complexité de chacun des composants
qui le composent.

B29. Correction

Un module est correct lorsque chacun de ses composants respecte et implémente sa


spécification (sous-spécification).
Il faut remarquer que la correction d’un programme ne peut pas être vérifiée par un
processus de test.

31
Des outils mathématiques de correction existent, mais, ils sont souvent difficiles et
longs à utiliser pour la plupart des concepteurs.
C’est la raison pour laquelle, il est plus pratique d’utiliser des méthodes de validation qui
reposent sur les formules des configurations ascendante et descendante vues ci-avant et
permettent de démontrer la correction pendant la construction des programmes et pendant
leur assemblage et d’alléger ainsi le processus de correction.
Sans recours à de telles méthodes ou à des tels outils, jamais un concepteur ne pourra
garantir l’absence des erreurs dans un système.

B30. Correspondance

Ce concept est un indicatif du degré de représentation des informations du monde réel au


sein d’un module ou de tout autre composant.
C’est le degré auquel un module modélise les éléments du monde réel, c’est-à-dire du
problème à développer.

Une modélisation du monde réel par un programme ne peut, par définition, être complète.
C’est pour cela, il sera généralement intéressant de construire les structures des
programmes et celles des données autour des aspects faiblement dynamiques de ce monde
réel au lieu de ses aspects fortement dynamiques.
Les programmes ou les modèles seront, de cette manière, plus résistants aux différents
changements, surtout simultanés, qui pourraient intervenir dans le monde réel.

Ex : il sera généralement intéressant de coder, dans un fichier, des dates de naissance au


lieu des âges, afin d’éviter d’aller le mettre à jour chaque année.

B31. Forte capacité de cacher l’information

Le travail de conception, de validation, de maintenance, ….relatif à un module donné


ne peut s’accomplir que sur base de ses seules structures internes.
Cela exige que ce module puisse :
- cacher et protéger fortement toutes ses structures de traitements et des données qui sont
propres à sa spécification, et
- dévoiler à l’extérieur les seules informations dont les autres modules ont besoin pour
établir un rapport correct avec lui.

Toute modification d’une de ces structures internes ne doit donc avoir aucun impact sur
les structures internes d’autres modules.
C’est ainsi qu’un module pourra posséder une forte capacité de cacher l’information.
Donc, plus grande est la capacité d’un module à se «barricader», moins intense est
l’effort à fournir pour analyser et le maintenir.

Toute conception qui utilise une seule relation faible de structuration (cfr ci-après) et
qui oblige généralement de raisonner en fonction d’un ensemble non cohérent d’objets,
d’informations ou de structures du domaine produit des modules dotés d’une très faible
capacité de cacher l’information.

32
REMARQUE IMPORTANTE :

Les 3 propriétés principales (conséquentes) d’un module sont, comme on l’a vu :


- une FORTE cohésion interne,
- une FORTE capacité de cacher l’information,
- un FAIBLE degré de couplage avec son environnement.

Donc, si un composant est défini comme un module, c’est-à-dire qu’il renferme une
abstraction, alors ce composant possède les 3 propriétés citées ci-haut. Mais, on peut
généraliser (parfois à tort) et accepter que tout composant qui possède les 3 propriétés citées
ci-haut est automatiquement un module.

C’est cet aspect général et nécessaire qui poussera la plupart des concepteurs à ressortir les
différents modules des architectures logicielles.

Nous aurons donc à faire face à des bonnes et à des mauvaises modularisations.

Les différents modules d’une architecture logicielle peuvent résulter de la décomposition


d’un composant correspondant, au point de vue «métier», à une unité fonctionnelle.

Une unité fonctionnelle, identifiée en analyse fonctionnelle ou issue d’une décomposition


peut contenir des fonctionnalités ou des abstractions assez distinctes ou dont les réalisations
dynamiques doivent être distinguées, et cela doit nécessiter une décomposition.

Plusieurs unités fonctionnelles, identifiées en analyse fonctionnelle ou issues d’une


décomposition peuvent contenir des fonctionnalités ou abstractions similaires (au point de vue
des résultats, de réalisation d’un concept, …) ou se présentant sous forme de sous-ensemble,
dans ce cas, il serait généralement profitable de procéder à un regroupement et de reproduire
un composant unique.

33
B3. CONCEPTS AU NIVEAU DE L’ARCHITECTURE LOGICIELLE

Une architecture logicielle est une structuration des différents composants du système à
développer à l’aide de certaines relations sémantiquement bien définies.
Ces composants sont :
- soit des composants principaux qui implémentent les différents traitements «métiers»,
- soit des composants auxiliaires qui servent principalement à réaliser les I/O du système,
à gérer le parallélisme, à gérer la synchronisation et le séquencement, à gérer la sécurité,
à gérer le recouvrement et la reprise des erreurs, …

Les concepts essentiels de ce niveau sont :

B31. Continuite et Maintenabilité

Il s’agit d’un ensemble des concepts destinés à contrôler le vieillissement d’un système.
Généralement, une architecture logicielle est en permanent changement jusqu’à ce que
l’on décide en raison de son degré d’obsolescence, de la recréer.

Le désordre (écart par rapport aux exigences de l’environnement) au sein d’un système
augmente donc avec le temps, des comportements spécifiques devront donc être prévus,
dans la méthodologie afin de maintenir le système et de réduire, dans un certaine
mesure, ce désordre.
Ces concepts sont aussi destinés à contrôler les performances et le rendement du
système en vue de l’optimisation éventuelle de son fonctionnement.

B32. Validité et Sécurité d’utilisation

Les concepts cachés derrière ces 2 termes servent principalement à examiner et à


contrôler la conformité de l’architecture logicielle par rapport aux spécifications et
décisions de l’analyse fonctionnelle.

B33. Robustesse

La robustesse d’une architecture logicielle exprime sa tolérance vis-à-vis de certaines


erreurs de l’environnement (provoquées ou venant de l’environnement).

B34. Consistence

Ce concept s’appuie sur le degré de conformité des éléments constitutifs du


raisonnement dans le développement d’un système lorsqu’il est confié à plusieurs
ingénieurs.

34
B35. Convivialité et Testabilité

Ces 2 concepts déterminent un ensemble des paramètres relatifs :


- à la souplesse, au caractère ergonomique et amical du système (guide d’utilisation,
paramétrage des motifs, utilisation des couleurs, …),
- à la capacité du système de se faire examiner ou tester par l’utilisateur final.

B36. Contrôle d’assemblage physique

Il s’agit d’un ensemble des concepts servant de guide lors de la descente de


l’architecture logicielle sur un environnement physique donné.
Les différents morceaux de cette architecture logicielle doivent devenir des entités
physiques selon les exigences de fonctionnement, de temps d’exécution, d’espace, de
performances, …

35
C. MODELISATION DU CYCLE DE VIE

Dans le but de produire efficacement les logiciels, le processus de développement des logiciels
exige plusieurs étapes fondamentales ordonnées et coordonnées qui déterminent leur cycle de vie.

Une Méthodologie de Développement de Logiciels est incluse dans un cycle de vie.


Un cycle de vie n’est donc qu’une organisation et une succession de ces étapes fondamentales
appliquées sur le développement d’un logiciel, de sa conception à sa disparition,
afin de pouvoir détecter rapidement les erreurs au plus tôt et de maîtriser la qualité du logiciel,
les délais de sa réalisation et les coûts associés.

C1. ÉTAPES ESSENTIELLES DE DEVELOPPEMENT

Le séquencement des étapes de développement et la répétitivité des activités de ces étapes


dépendent essentiellement du cycle de vie choisi et de la maîtrise de cycle de vie.
Les principales étapes d’un cycle de vie sont :

C11. ÉTUDE DE FAISABILITE

Cette étape revisite les besoins de l’utilisateur pour mettre en évidence :


- les domaines d’expertise,
- les objectifs du projet afin de bien circonscrire la finalité du projet et son inscription
dans une stratégie globale,
- l’état actuel de l’environnement et des ressources disponibles (organisation du travail,
répartition des tâches, répartition géographique, …),
- les qualités fonctionnelles attendues (en termes des services offerts et des métiers),
- les qualités non fonctionnelles attendues ou sous-entendues (efficacité, ergonomie,
robustesse, sécurité, …),

C12. SPECIFICATION ET PLANIFICATION

Cette étape prend appui sur les considérations techniques exigées par le développement
et sur la faisabilité informatique pour produire une description générale (spécification
globale) de ce que doit accomplir le système à développer.
Elle devra nommer et spécifier chaque projet de ce système.

Une fois que la description générale est acceptée par l’utilisateur, une planification de
réalisation des travaux incluant toutes les tâches (développement, acquisition des
matériels, acquisition des locaux, formation, information, …) devra être établie et
adoptée.

36
C13. CONCEPTION GLOBALE

Cette étape a 2 grands objectifs :


1. analyser chaque projet afin de le décomposer et ressortir éventuellement ce qui
s’apparente à une application (cfr ci-avant),
2. concevoir chaque application en définissant clairement chacun de ses composants de
base.
Le résultat de cette conception est que l’application est organisée en grandes
fonctionnalités (métiers ou autres) qui sont chacune dotée d’une spécification
informelle.

Cette étape about à la construction des architectures logicielles pour chaque


application.

C14. CONCEPTION DETAILLEE

Cette étape doit expliciter les moyens par lesquels chaque composant sera mis en
œuvre en s’occupant du mode de réalisation de chaque élément mentionné dans
l’architecture globale.
La conception détaillée enrichit les spécifications informelles avec des détails de mise
en œuvre afin d’aboutir à des spécifications concrètes.
Etant donné une architecture physique et des outils «software» donnés, ces
spécifications concrètes devront permettre la production des algorithmes et une prise
en charge adéquate des données.

C15. CODAGE, VALIDATION ET INTEGRATION

Au sein de cette étape :


- les algorithmes doivent être codés,
- la validation doit être accomplie à l’aide des tests unitaires, des tests d’intégration ou
des démonstrations.

C16. QUALIFICATION ET DOCUMENTATION

Il s’agit, par intervalles plus ou moins réguliers, de procéder à la vérification de la


conformité des produits intermédiaires ou du produit final par rapport aux
spécifications de l’analyse fonctionnelle, et de documenter efficacement tout le cycle
de vie pour une utilisation adéquate du logiciel et pour des développements futurs
(cfr gestion de configuration des projets).

37
C17. INSTALLATION, EXPLOITATION ET MAINTENANCE

Cette étape installe le système graduellement ou non sur toute ou une partie de la
configuration.
Le système automatisé peut ainsi être utilisé et subir une ou plusieurs maintenances.
Une maintenance peut être :
- adaptative : pour adapter le système développé à un nouvel environnement hardware
ou software pourvu des paramètres nouveaux ou plus contraignants,
- corrective : pour corriger certaines erreurs de développement qui exigent de
recommencer complètement certaines étapes du cycle de vie,
- perfective : pour incorporer au système développé des améliorations demandées par
l’utilisateur.

Les coûts des étapes importantes d’un cycle de vie, se répartissent, en moyenne, comme suit :

Type de Système Conception Conception Codage, Validation


Globale Détaillée et Intégration

GESTION 44 % 27 % 27 %

SCIENTIFIQUE 43 % 25 % 29 %

INDUSTRIEL 46 % 18 % 32 %

38
C2. MODELES DE CYCLE DE VIE

Dans la façon de gérer les différentes étapes de développement citées ci-haut, il y a à ce jour
5 grandes classes de modèles remarquables de cycle de vie :

C21. MODELES DE LA CASCADE

Le fondement de base de ces modèles est de considérer que l’on peut, avant de
commencer le développement, définir complètement et en détails tous éléments
nécessaires à la réalisation du projet
(cela n’est peut-être pas réaliste quel que soit le projet).
Le principe en est alors que chaque étape du développement doit se terminer à une date
déterminée et produire, à cette date, certains documents, logiciels ou parties de logiciels.
L’étape suivante ne peut démarrer que ssi les résultats de l’étape qui vient de se terminer
sont jugés satisfaisants (cfr réunions tactiques, stratégiques ou techniques).

Ces modèles, en principe, n’admettent pas de retour en arrière et tout le cycle de vie reste
donc fondamentalement linéaire.

On remarque dans ces modèles que plus le projet avance, plus les risques qui peuvent
faire échouer ou retarder le projet diminuent.

La plupart des démarches et activités de ces modèles sont trop techniques et sont basées
sur un processus de contrôle de la qualité des produits intermédiaires qui exclue
l’intervention de l'utilisateur pendant pratiquement tout le cycle de vie pour n’attendre
son approbation que sur le produit final.

Les modèles en cascade sont adaptés aux projets de petite durée (inférieure à 1 an).
Pour de gros projets, ils sont mal adaptés et présentent le gros désavantage de ne pas
procéder à une conception simultanée de composants et des modules.

C22. MODELES EN V (EN Y)

Le principe de ces modèles est que toute décomposition de composants est accompagnée
d’une recomposition (vérification de la traçabilité des spécifications).
En conséquence, toute description de composant est accompagnée des tests qui
permettront de s’assurer qu’il correspond bien à sa description.
Cela évite que l’on énonce une propriété, dans une spécification, qui soit impossible de
vérifier objectivement plus tard.

Le processus reste linéaire, mais les étapes et les activités d’acceptation, de validation et
de vérification sont préparées et conditionnées dès les étapes et les activités de
conception et de construction afin de mieux approfondir celles-ci.
Ainsi, par exemple, c’est pendant la conception globale que les tests d’intégration sont
préparés et conduits afin de mieux recentrer cette conception globale en cas d’erreurs
(donc, retours en arrière ciblés et localisés) .

39
Chronologiquement, ces modèles se présentent comme suit :

Etapes et les activités de Etapes et activités d’acceptation,


conception et de construction (1) de validation et de vérification (2)

Les produits intermédiaires, dans ces modèles, apparaissent mieux et peuvent être
rectifiés à temps pour l’acceptation du produit final.
La cohérence entre (1) et (2) permet de vérifier en continu que le projet progresse vers un
produit répondant effectivement aux besoins définis dans l’analyse fonctionnelle.

Ces modèles sont adaptés aux projets de taille et de complexité moyenne.


Ils sont une amélioration de la plupart des modèles en cascade. Ils permettent d'identifier
et d'anticiper très tôt les éventuelles évolutions des besoins, et impliquent fortement
l’engagement de l’utilisateur dans le cycle de vie car il est un élément central dans les
étapes et activités de préparation de jeux de test (test de recettes, …).

Les modèles en Y sont une forme d’amélioration des modèles en V qui dissocient la
résolution des questions fonctionnelles et techniques pour mieux les approfondir.

40
C23. MODELES EN SPIRALE

Un modèle en spirale a le même fondement qu’un modèle en V, mais il met l’accent sur
l'analyse des risques et est beaucoup plus général que la plupart des autres modèles et
peut même les inclure.

Dans le cycle de ces modèles, il y a 4 phases essentielles :


1. détermination des objectifs, des alternatives, des contraintes à partir des résultats du
cycle précédent ou des besoins initiaux,
2. analyse des risques, évaluation des alternatives, prototypage éventuel,
3. développement et vérification de la solution retenue (modèles de la cascade ou en V);
4. revue des résultats et planification du cycle suivant.

Les modèles en spirale utilisent systématiquement des prototypes exploratoires afin de


guider la conception de la solution retenue en phase 3.
Il est plus difficile à mettre en œuvre car il se présente comme un méta-modèle et est
particulièrement adapté aux projets innovants, à grands risques et dont les enjeux sont
très importants.

Les risques majeurs de la phase 2 sont :


- défaillance du personnel,
- calendriers et budgets irréalistes,
- développement de fonctions inappropriées,
- développement d’interfaces d’utilisateurs inappropriées,
- volatilité des besoins,
- manque de composants externes,
- exigences démesurées par rapport à la technologie,
- problèmes de performances.

C24. MODELES PAR INCREMENT

Dans tous les modèles précédents, les étapes sont organisées de manière qu’un logiciel
est, au fil de ces étapes, décomposé en composants plus ou moins élémentaires qui sont
développés séparément puis intégrés à la fin du processus.
Dans les modèles par incrément, un seul ensemble de composants est développé à la fois
et des incréments viennent s’intégrer (s’emboîter) à un noyau des fonctionnalités
développées ou formées au préalable.
Les incréments sont généralement des maquettes ou des prototypes.
Chaque incrément peut donner lieu à un cycle de vie classique plus ou moins complet,
càd que chaque ensemble de composants peut être développé selon un des modèles
précédents.

Ces modèles par incrément ont été proposés afin de lutter contre certaines dérives
bureaucratiques et contre l’impossibilité de procéder de manière linéaire dans certains
gros projets.

41
Les grands avantages de ces modèles par incrément sont :
- chaque développement est beaucoup moins complexe,
- les intégrations sont progressives,
- grande possibilité de mise en œuvre ou mise en service du noyau après chaque
incrément, le produit peut être ainsi livré en plusieurs fois de manière incrémentale en
le complétant au fur et à mesure sur base des incréments.

Les risques majeurs en sont :


- mise en cause du noyau ou des incréments précédents,
- impossibilité d’intégrer des nouveaux incréments.

En vue de diminuer ces risques, les incréments doivent être aussi indépendants que
possible fonctionnellement, mais aussi sur le plan du calendrier de développement.

La mise en œuvre de ces modèles exige une grande expérience dans la conduite des gros
projets.

C25. METHODES AGILES

Les méthodes de développement dites «agiles» visent à accélérer le développement des


logiciels.
On développe ainsi une version minimale rapide du système à livrer et on y intègre les
fonctionnalités par un processus itératif basé sur une écoute client et des tests tout au
long du cycle de développement.

C’est le risque lié à l'instabilité de l'environnement technologique et au fait que le client


est souvent dans l'incapacité de définir ses besoins de manière exhaustive dès le début du
projet qui ont favorisé l’émergence des méthodes agiles.

Ainsi, les 4 grands principes des méthodes agiles sont :


- individus et interactions plutôt que processus et outils,
- développement logiciel plutôt que documentation exhaustive,
- collaboration avec le client plutôt que négociation contractuelle,
- ouverture au changement et acceptation des spécifications changeantes plutôt que suivi
d’un plan rigide.

Dans ces méthodes agiles, le client est pilote à part entière de son projet et obtient très
vite une première mise en production de son logiciel et est associé au projet dès son
démarrage.

42
Les méthodes agiles les plus connues sont :
- le RAD (Rapid Application Development),
- le DSDM (Dynamic Software Development Method),
- l’UP (Unified Process),
- le RUP (Rational Unified Process),
- le XP (eXtreme Programming).

REMARQUES

- il n’existe pas de modèle idéal. Un modèle est bon s’il est appliqué adéquatement .
- le modèle en cascade ou en V est risqué pour les développements innovants dans lesquels les
spécifications et la conception sont souvent remis en cause et comportent donc un grand
risque d’être inadéquates,
- le modèle incrémental ne donne pas beaucoup de visibilité sur tout le processus complet,
- très souvent, un même projet sera obligé de mêler dans son cycle différentes approches afin
d’appréhender plus facilement ses différentes parties, par exemple, le prototypage pour ses
parties à haut risque et la cascade pour les parties stables, bien connues et à faible risque.

43
D. GRANDES TENDANCES ACTUELLES

D1. CARACTÉRISTIQUES ESSENTIELLES

Une méthodologie de développement des logiciels doit donc intégrer des procédés
efficients et efficaces d e fabrication des logiciels de façon à en garantir la qualité,
la correction et le respect des coûts et des délais.

La qualité du logiciel comprend principalement :


- la validité : condition du logiciel qui a implémenté les spécifications définies par
l’analyse fonctionnelle,
- la fiabilité : condition d’un logiciel à assurer la disponibilité du service attendu,
- la robustesse : condition d’un logiciel à recouvrer des erreurs de l’environnement et
à travailler dans des conditions dégradées,
- la réutilisabilité : condition d’un logiciel à être fonctionnellement réutilisable en tout
ou en partie dans une autre version du système ou de
l’environnement,
- l’efficacité : aptitude d’un logiciel à bien utiliser et à bien consommer des
ressources (temps, espace, …),
- la portabilité : facilité d’un logiciel à être réutilisé dans des nouveaux
environnements,
- la maintenabilité : condition d’un logiciel d’être résistant à certains changements de
spécifications,
- la sûreté : condition d’un logiciel à pouvoir être exploité de manière sécurisée, en
toute intégrité et en toute confiance.

Les différents aspects servant à garantir la qualité, la correction et le respect des coûts
et des délais peuvent parfois se révéler contradictoires, c’est au développeur qu’il
revient la responsabilité de les pondérer et de les expliquer selon les circonstances.

Les méthodologies de développement des logiciels connues à ce jour utilisent des


méthodes qui vont des démarches empiriques (basées essentiellement sur des
expériences vécues) aux démarches très formelles (approches transformationnelles,
démonstratives, …).

Toutefois, en définitive, une méthodologie de développement des logiciels s’articule


principalement autour :
- des PRINCIPES de base,
- des TECHNIQUES SPECIALISEES, qui s’appuient sur ces principes de base,
- des METHODES, qui organisent plusieurs techniques spécialisées en un ensemble
cohérent,
- des METHODOLOGIES (Outils ou Ateliers), qui supportent ces méthodes.

44
D2. PRINCIPES

Les 5 grands principes qui guident un démarche de développement des logiciels sont :

- la RIGUEUR
Bien qu’il y ait une certaine part de CREATIVITE dans l’activité de production
des logiciels, une certaine RIGUEUR reste exigée en vue d’augmenter la qualité
des logiciels produits.
Le niveau maximal de la rigueur exige des descriptions et des validations qui
s’appuient sur des notations et des lois mathématiques.
La FORMALISATION est déjà un premier et grand pas dans la rigueur, car la
formalisation permet de mesurer les concepts, le mesurage permet de contrôler, le
contrôle permet de gérer et la gestion permet d’améliorer,

- la SEPARATION DES PROBLEMES


Ce principe issu du bon sens demande à considérer séparément les différents
aspects du problème posé, à l’aide d’un ou de plusieurs CRITERES de
séparation ou de regroupement, pour mieux maîtriser sa complexité.
Ces critères sont variés et peuvent se baser sur :
le TEMPS (les ≠ aspects sont abordés successivement, répartition des éléments du
système opérant dans le temps),
l’IMPORTANCE des éléments des sous-groupes formés (Noyau et extensions),
la QUALITE des éléments du problème,
les RESSOURCES utilisées (bases de données, structures, …),
le REGROUPEMENT par «produit livrable»,
la REUTILISATION ou la SIMILARITE des opérations,
la COHERENCE technique (au point de vue de données propres à 1 sous-ensemble),
la COHERENCE d’organisation (au point de vue de «services», par ex.),
la COHERENCE fonctionnelle (au point de vue de gestion d’un «produit livrable»),
la COHERENCE des responsabilités,
le comportement concurrent (par rapport à d’autres comportements), …

- la MODULARITE
Ce principe permet, à un niveau d’abstraction donné, de ne considérer que les
seuls aspects jugés importants dans un problème et aussi d’encapsuler les
abstractions naturelles du problème dans des unités fonctionnelles séparées en vue
de maîtriser leur contenu et les relations créées entre ces unités fonctionnelles
(modules).
Ce principe permet finalement d’aboutir à une architecture stable dotée d’une
très forte maintenabilité.

- la GENERICITE
La généricité permet d’appliquer un raisonnement général sur le problème visé
afin que la solution issue :
1. soit paramétrable et adaptable pour être implémentée dans plusieurs cas
spécifiques,
2. anticipe certains changements dûs à l’évolution de l’environnement et de ses
besoins.
La formulation d’une théorie plus ou moins complète issue du problème à résoudre
constitue une bonne base pour une généricité forte.

45
La généricité permet d’augmenter la maintenabilité du logiciel et dans une certaine
mesure sa portabilité et sa réutilisabilité.

- la CONSTRUCTION INCREMENTALE
Ce principe est une approche dans la quelle le logiciel final est obtenu par
raffinements successifs des aspects de plus en plus secondaires sur une
construction de base ou une construction primaire. On se focalise ainsi, à chaque
niveau d’abstraction, sur l’essentiel.

46
D3. TECHNIQUES

- LECTURE en AVANT
Dans beaucoup de cas, l’accès sélectif à une donnée précise dans un fichier peut
être considéré comme un cas particulier de l’accès séquentiel.
Lorsque les données doivent être accédées séquentiellement, la technique de la
lecture en avant (Forward Reading) permet de considérer plus d’un enregistrement
logique à chaque cycle de traitement par un accès préventif.
Le traitement des données est ainsi simplifiée et son efficacité augmente.

- DIVISER pour REGNER


Technique qui se base essentiellement sur le principe de séparation des problèmes
pour produire des sous-problèmes maîtrisables à partir du problème posé ou d’un
autre sous-problème.
Lorsque les critères de division sont adéquats, cette technique permet d’améliorer
considérablement la visibilité du problème et des sous-problèmes.

- BOITE NOIRE
Cette technique permet de rendre temporairement invisibles un problème ou un
sous-problème pour se focaliser principalement sur la structure des données
reçues et des données renvoyées au sein d’un ensemble articulé de sous-
problèmes.
Cette focalisation sur la structure de données échangées permet de concevoir et de
développer très indépendamment chaque problème ou sous-problème.

- HIERARCHISATION
La technique d’hiérarchisation permet de répartir, sur base de la relation de
séquence, de répétition ou de sélection les composants d’une solution
ou d’un problème dans des niveaux hiérarchiques d’une structure.
Un niveau hiérarchique peut contenir un ou plusieurs composants de la solution ou du
problème, mais chaque composant ne devrait appartenir qu’à un seul niveau hiérarchique.

47
D4. CLASSES DE METHODOLOGIES

Une méthodologie de développement des logiciels concerne essentiellement les étapes


de conception globale et de conception détaillée du problème étudié et doit respecter
les 5 grands principes du génie logiciel.
Les grandes démarches méthodologiques actuelles de développement des logiciels ont
été distribuées, par beaucoup d’auteurs, dans 5 grandes classes de méthodologies qui
sont :
- le développement fonctionnel (Functional Decomposition),
- le développement composé (Data Flow Design),
- le développement structurel (Data Structure Design),
- le développement démonstratif (Calculus Programming),
- le développement orienté objet (Object Oriented Design).

Chacune de ces classes de méthodologies construit une STRUCTURE du PROBLEME


à résoudre en s’appuyant sur une ou plusieurs de ses caractéristiques pour aboutir à
des MODULES (Composants) d’ACTION ou d’OBJET.

Les 5 classes fournissent une centaine de méthodologies connues toutes confondues.


Il n’ya pas de meilleure méthodologie de développement de logiciels Une méthodologie de
développement de logiciels est bonne si elle est adéquatement appliquée face à un problème
donné. Il n’est pas rare que des concepteurs appliquent plusieurs méthodologies différentes
ou plusieurs outils appartenant à différentes méthodologies lors du développement d’un
système donné.

Toutefois, une méthodologie complète de développement de logiciels devrait :


1. inclure une procédure rationnelle pour partitionner et modéliser les aspects utiles du
système à développer,
2. partitionner aussi bien le système à développer que le processus de conception lui-même,
3. garantir la correction du produit final par la correction de chacun des résultats
intermédiaires,
4. être plus ou moins résistante aux innovations dans le domaine d’analyse et de
programmation par une forte intégration des principes de base de génie logiciel.

48
1. Le développement fonctionnel (Functional Decomposition)

Cette classe de démarches méthodologiques est guidée par l’application de la seule


technique de «Diviser pour Régner» qui permet de manière récursive de :
- partitionner le problème original (ou un sous-problème) en sous-problèmes de
taille ou de complexité sensiblement inférieure à celle du problème original (ou du
sous-problème),
- solutionner chaque sous-problèmes, si cela est possible,
- combiner les solutions issues de tous les sous-problèmes pour constituer la
solution du sous-problème intermédiaire du niveau immédiatement supérieur ou
du problème original.

Chaque sous-problème, à un niveau de la hiérarchie, peut donc être vu comme un


regroupement logique d’un certain nombre de composants cohérents et maîtrisables
en termes de fonctionnalités, de classes, d’associations, d’événements, de
contraintes, de propriétés, de localisation spatiale, …

C’est donc une approche TOP-DOWN mais qui ne fournit aucune base solide pour
le choix des critères de décomposition.
Une des conséquences est qu’un même problème à développer confié à plusieurs
développeurs conduira certainement à autant de structures diverses de solutions car
chacun des développeurs appliquera, aux différents points et niveaux de
décomposition, ses propres critères de décomposition.
Cette méthodologie conduit généralement à de bons résultats pour des logiciels de
taille moyenne.

Pour des logiciels de très grande taille, elle risque de produire des programmes
inefficaces ou mauvais si l’inadéquation entre les critères de décomposition utilisés
et la nature du problème à décomposer est trop grande.
Cas où l’on aboutit très souvent à une DECOUPE purement FONCTIONNELLE
du problème et qui est dotée d’un phénomène de TELESCOPAGE (clash) important.
Dans un phénomène de télescopage, il peut exister un grand nombre de composants
dans la structure qui ont inutilement un très grand degré de couplage entre eux.
Dans un phénomène de télescopage, il y a un risque permanent que la
structure du logiciel ne corresponde pas à la structure du problème, ce qui induit
souvent des programmes inefficaces ou mauvais.

A cause de l’existence de ce risque, cette méthodologie exige généralement que le


travail de la modélisation du problème (production de la structure du problème) et de
la production des programmes (génération d’une structure de solution) soient
simultanés afin de corriger à temps les inefficacités éventuelles.

En fin de compte, cette classe de méthodologies conduira à un système logiciel


découpé en COUCHES et/ou en PARTITIONS et permettra d’aboutir à des
modules d’action.

49
2. Le développement composé (Data Flow Design)

Cette classe de démarches méthodologiques se base essentiellement sur l’analyse de


tous les flux de données existant dans le problème posé.

Un flux de données est un regroupement logique de données auquel un sens a été


conféré.
La structure du problème se présente alors comme un graphe de flux de données
orienté dans lequel chaque sous-structure est obtenue en appliquant la technique de
la BOITE NOIRE à un ou à plusieurs flux de données du problème.
Chaque boîte noire devient alors un TRANSFORMATEUR de flux de données
d’entrée en un flux de données de sortie correspondant et se présente comme un
composant du système dans lequel l’enrichissement, l’utilisation et la manipulation
des informations peuvent s’accomplir.

Le graphe de flux de données :

A B C D
X1 X2 X3

signifie que la boîte noire X1 transforme le flux de données d’entrée A en flux de


données de sortie B,
que la boîte noire X2 transforme le flux de données d’entrée B en flux de
données de sortie C,
que la boîte noire X3 transforme le flux de données d’entrée C en flux de
données de sortie D.
Chaque Xi est donc un composant qui peut être décomposé en sous-composant
d’acquisition de données, sous-composant d’enrichissement ou de transformation
de données et sous-composant de transfert de données.

Les 3 grandes étapes des méthodologies de cette classe pour la conception générale
et la conception détaillée sont :
- modélisation des données à l’aide d’un graphe de flux de données (GfD),
- identification des éléments afférents, efférents et centraux de transformation.
Un élément central de transformation porte un ou plusieurs thèmes mentionnés
dans la spécification du problème,
- dérivation de la structure des programmes à partir de ces éléments.
Il s’agit de subdiviser ou de regrouper ces éléments de façon à générer une
structure hiérarchique qui peut être raffinée et optimisée.

Cette classe de démarches méthodologiques, pour de très grandes fonctionnalités,


tendra à produire un réseau plutôt qu’une hiérarchie de programmes, mais elle
n’exige pas généralement pas que le travail de la modélisation du problème
(production du DfD) et de la production des programmes soient simultanés.
On aboutit, avec cette classe, à des modules d’action.

50
3. Le développement structurel (Data Structure Design)

Le fil conducteur de cette classe de démarches méthodologiques développées


principalement par Michaël Jackson et J.D. Warnier est qu’un programme «voit» le
monde à modéliser essentiellement à travers les structures de ses données.

Ce qui signifie qu’un modèle correct des structures de données relatives à un


problème peut être transformé en un programme qui incorpore un modèle correct du
réel perçu et qu’une spécification incorrecte des structures de données produira des
programmes mauvais, incorrects ou difficilement maintenables.

Cette vision établit donc le concept de correspondance dans les différents modules
du produit final issu de cette classe; un programme qui ne correspond pas
directement à l’environnement du problème a beaucoup de chance d’être mauvais.

Elle affirme aussi que lorsque la structure du programme dérive de la structure de


ses données, la relation entre les différents composants de l’architecture générée est
une relation très riche pour la maintenabilité et la continuité modulaire.

On dit généralement que le concept de consistance est largement vérifiée au sein de


cette classe, car 2 concepteurs travaillant sur le même problème ont beaucoup de
chances d’aboutir à des structures de programmes qui sont essentiellement les
mêmes.

La conception générale et la conception détaillée dans cette classe suivent les 5


étapes suivantes :
1. modélisation de l’environnement du problème à l’aide d’un diagramme de réseau
du système (DRS),
2. définition et vérification des structures de données générées à l’entrée et à la
sortie,
3. dérivation et vérification des structures de programmes,
4. dérivation et allocation des composants et opérations élémentaires (éléments
directement exécutables par le processeur final),
5. écriture des textes de structures et de programmes.

Le DRS représente, dans sa plus simple expression, un réseau de fonctions qui


consomment , transforment et produisent des fichiers ou de données séquentiels.
Si l’allocation des opérations élémentaires entraîne la génération des structures
complexes (non directement exécutables par le processeur final) alors il existe dans
le modèle des mauvaises structures de données ou des structures de données
non achevées.

L’étape 2 exige une connaissance approfondie du problème à développer et de ses


spécifications et constitue donc le nœud central de cette classe de méthodologies.
Une des tâches de cette étape est de déterminer les correspondances entre les
structures.
Les correspondances 1-1 les plus courantes (où les éléments A et B sont issues de 2
structures de données différentes) sont :

51
A B Consommer A pour produire B


*
A B ou

* *
A B ou

* Consommer A pour produire B *


A B 

°
A B ou

° °
A B ou

° Consommer A pour produire B °


A B 

° *
A B ou

* ° Tant que C faire °


A B  Consommer A pour produire B

Les phénomènes de TELESCOPAGE apparaissent ici comme étant des correspondances


1-N et N-1 où respectivement les éléments de structures cibles et origines sont situés dans
la même définition de structure :

52
A B B A

ou
C C

Ces correspondances 1-N et N-1 peuvent générer des composants dotés d’un degré élevé
de couplage car, par exemple, le graphe de gauche peut signifier :
- consommer A pour produire B et C, ou
- consommer A pour produire B ou C, ou
- consommer une partie de A pour produire B et le reste de A pour produire C.

Ces phénomènes de TELESCOPAGE appauvrissent donc généralement les structures des


programmes et obligent souvent les concepteurs à y apporter des modifications, des
optimisations ou des améliorations plus ou moins importantes.

Par ce développement, on aboutit essentiellement à des modules d’action.

53
4. Le développement démonstratif (Calculus Programming)

Les méthodologies de cette classe regroupent un ensemble des méthodes, des démarches
et des outils qui, à l’aide des assertions (prédicats, invariants, post-conditions,
pré-conditions, …) permettent de démontrer formellement les programmes.

Une des principales tâches dans les méthodologies de cette classe est de spécifier
formellement une post-condition ou une pré-condition de départ pour en dériver par
raffinements successifs des assertions intermédiaires et des instructions y relatives.

Le résultat voulu par la spécification d’un problème peut donc servir naturellement de
post-condition de départ.

Le développement à déployer reste donc une philosophie TOP-DOWN dans laquelle les
programmes et les prédicats résultants sont formés de proche en proche autour de
l’assertion de départ voulu.

Toutefois, ce développement nécessite une bonne connaissance de la logique et des


concepts mathématiques y relatifs.
Il est un processus plus lent que la plupart des méthodes des autres classes et il produit
généralement une démonstration beaucoup plus longue que la taille du logiciel à
construire, c’est pour cela que le développement démonstratif n’est pratiquement utilisé
que pour le développement des programmes critiques.
Néanmoins, le développement démonstratif conduit toujours (lorsqu’il n’y a aucune erreur
de démonstration) à des programmes corrects, fiables et d’une élégance presque absolue
qui n’ont plus besoin d’être testés.

On aboutit, par les méthodologies de cette classe, à des modules d’action.

54
5. Le développement orienté objet (Object Oriented Design)

Les méthodologies de cette classe (G. Booch, méthode agile, SCRUM, …) regroupent un
ensemble des méthodes qui :
- consolident l’analyse des données avec l’analyse des traitements,
- tendent à généraliser la notion de réutilisabilité et de portabilité
(utilisation massive des packages),
- tendent à établir fortement la notion de correspondance en essayant de simplifier les
transformations entre le niveau conceptuel et l’implémentation physique.

Dans ce développement, l’architecture logicielle n’est plus liée aux seuls traitements du
domaine mais à tous les objets (actifs et passifs), c-à-d aux données et traitements de ce
domaine.
On aboutit ainsi à une décomposition du système à développer en composants qui peuvent
être conçus et développés indépendamment au point de vue des données et des traitements
et dont les maintenances sont encore plus localisées et encore plus faciles à cerner.
On atteint aussi une certaine stabilité lors d’une évolution éventuelle des fonctionnalités
ou des structures de données et une grande abstraction sur les données et sur les
traitements car toute action, du point de vue de celui qui la déclenche, est atomique.

La plupart des instructions issues de ce développement impliquent l’envoi des messages à


des objets, objets qui peuvent réagir à ces messages et changer éventuellement leur état.

Le processus de construction de l’architecture logicielle (issue de la conception globale)


passe par :
- la modélisation du problème à travers la notion de classes et la notion d’instanciation de
classes,
- la définition des responsabilités de chaque classe (par la définition des messages et
méthodes de chaque classe).

Cette classe fait généralement appel à des outils de développement qui appartiennent à
d’autres classes pour implémenter les méthodes d’objets et produit essentiellement des
modules d’objet.
Une des grandes tâches dans toutes les démarches OOD est de pouvoir loger chacune des
responsabilités du domaine correctement dans des classes d’objets afin de bénéficier de
tous les avantages de l’encapsulation et de l’empaquetage.

a. Transformations et traductions visibles de schémas

Les règles de conception et de traduction les plus remarquables en OOD sont :

- Une classe OOD devient une module objet (un fichier ou un objet du langage) :

-x : int
+y:String
#z:int

+m(p:short):boolean
+k(q:String)

55
Public class A
{ private int x ;
public String y;
protected int z;
public Boolean m (short p) {…}
public static void k (String q) {…}
} (pour une classe réelle)
Ou
Abstract public class A {…} (pour une classe abstraite).

Les attributs deviennent naturellement des variables de type primitif (ex : int)
ou de type fourni par la plate-forme et l’IHM utilisées (ex : String, Date, … en Java
à l’aide de l’importation des bibliothèques adéquates),

- une interface devient un objet interface :


<interface>
I

+aff( )

interface A
{…
Void aff( ) ;
…}

Une classe OOD peut implémenter plusieurs «objets» interfaces,

- un package devient un objet package :

Maintenance

- une association navigable (avec cible 0..1 ou 1..1) telle que :

A
x : int 0..1 ou 1..1 B
y : String
m(p:short)

se traduira en :
public class A
{ private int x;
private B v; (l'association devient une variable v de type B)
private m (short p); }
Les éléments contenus dans la classe B seront spécifiés dans "public class B",

56
- une association navigable (avec cible 0..* ou 1..*) telle que :

A B
x : int 0..* ou 1..* y : String

m(p:short)
k(q:String)

se traduira en :
public class A
{ private int x;
private B v[ ]; (l'association devient un tabeau v de type B)
private m (short p);
private k (String q) }
Les éléments contenus dans la classe B seront spécifiés dans "public class B",

- une association navigable (avec cible 0..1 ou 1..1 ordered) telle que :

A B
x : int 0..* ou 1..* y : String
[ordered]
m(p:short)
k(q:String)

("ordered" exprime que les différentes instances de B sont ordonnancées


selon un critère déterminé) deviendra :

public class A
{ private int x;
Private List<B> v = new ArrayList<B>( );
(l'association devient une variable d'instance de type
COLLECTION (ArrayList) si l'on doit respecter un ORDRE et
récupérer les objets de la classe B à partir d'un INDICE entier)
private m (short p);
private k (String q) }
Les éléments contenus dans la classe B seront spécifiés dans "public class B",

57
- une association navigable (avec cible 0..* ou 1..* qualifiée) telle que :

A B
x : int z 0..* ou 1..* z : String

m(p:short)
k(q:String)

(z étant un qualificatif) deviendra :

public class A
{ private int x;
private Map<B,z> v = new HashMap<B,z>( );
(l'association devient une variable d'instance de type
COLLECTION (HashMap) si les objets de B doivent être
récupérés à partir d'une CLEF ARBITRAIRE)
private m (short p);
private k (String q) }
Les éléments contenus dans la classe B seront spécifiés dans "public class B",

- un héritage tel que :

A B
x : int z : String

m(p:short)
k(q:String)

deviendra :

public class A extends B


{ private int x;
private static void m (short p);
private void k (String q) }

Les éléments contenus dans la classe B sont spécifiés dans "public class B",

58
- l’implémentation telle que :

LIVRE <interface> EMP


-titre:String
-auteur:String +emprunter( )
-isbn:ISBN +retourner( )
+imprimer( )
+emprunter( ) <interface> IMP
+retourner( )
+imprimer( )

deviendra :

public class LIVRE implements EMP, IMP


{ private String titre;
private String auteur;
private ISBN isbn;
public void imprimer ( ) {…} ;
public void emprunter ( ) {..};
public void retourner ( ) {…} },

- la dépendance telle que :

Maintenance Production
+Acier +Fer
+Or

ne peut se traduire que de manière indirecte par des directives d’importation


ou d’usage par :
package Maintenance;
import Production;
public class Acier
{ private Production Fer;
private Production Or }

59
- une association bidirectionnelle telle que :

HOMME FEMME
Nom:String Mari épouse Nom:String
0..1 0..1

deviendra :

public class HOMME


{ private FEMME épouse;
Public String Nom }

et

public class FEMME


{ private HOMME mari;
Public String Nom },

- une association récursive (réflexive) telle que :

HOMME 1..1
Nom:String chef

subordonne 0..*

deviendra :

public class HOMME


{ private HOMME subordonné[ ];
private HOMME chef;
public String Nom },

- la composition (et dans une mesure moins forte, toute autre notion de classe
imbriquée) telle que :

VOITURE MOTEUR

-modele:String 1..1 -puissance :int

deviendra :
public class VOITURE
{ private String modele;
Private MOTEUR x
{ private int puissance; }
},

60
- une classe d’association, comme dans le modèle suivant :

HOMME SOCIETE
Nom:String employé employeur Nom:String
0..* 0..*

EMPLOI
titre:String
salaire:double

se traduira comme suit :

public class EMPLOI


{ private String titre;
private double salaire;
private HOMME employé;
private SOCIETE employeur; …. }

Le concept de classe d’association a été donc traduit en classe normale à laquelle ont
été ajoutées des variables de type référence.

61
b. Les Modèles de conception

L'idée de modèles de conception est de définir un certain nombre de comportements à


l’aide des modèles et des techniques afin de pouvoir les réutiliser pour solutionner
des problèmes courants.

Les objectifs principaux des modèles de conception sont :


- diminuer le temps consacré au développement d'un logiciel,
- réduire les interactions qu'il peut y avoir entre les différentes classes (modules)
au sein d'un programme.

On pourra ainsi augmenter la qualité du logiciel par la puissance de la


réutilisabilité en appliquant à des nouveaux problèmes des solutions et des
modèles pré-existants de conception.
Pour qu’un modèle de conception participe efficacement au développement d’autres
problèmes, il est important de respecter le principe d’orthogonalité, qui exige que
chaque modèle de conception corresponde à une abstraction unique et différente
des abstractions ou stratégies utilisées dans d'autres modèles de conception.

Sans spécifier les détails spécifiques d’un domaine particulier, un modèle de


conception décrit, sous une forme abstraite, une solution standard pour répondre à un
problème d'architecture logicielle et de conception des logiciels,.

Techniquement, un «modèle de conception» doit être décrit par :


- un nom,
- une description du problème à résoudre,
- une description de la solution (le modèle de conception), c-à-d les éléments de la
solution avec leurs relations,
- l’ensemble des résultats générés par le modèle de conception.

En gros, il existe 3 classes de modèles de conception :


- classe de constructions qui définit la configuration des classes, des objets,
des instanciations, et qui contient principalement la Fabrique abstraite,
le Monteur, la Fabrique, le Prototype et le Singleton,
- classe de structures qui définit l’organisation des classes d'un programme dans
des structures plus complexes (séparant l'interface de l'implémentation), et qui
contient principalement l’Adaptateur, le Pont, l’Objet composite, le Décorateur,
la Façade, le Poids-mouche et le Proxy,
- classe de comportements qui définit les manières d’organiser les objets pour que
ceux-ci collaborent (distribution des responsabilités) et explique le
fonctionnement des algorithmes impliqués. Elle contient principalement
la Chaîne de responsabilité, la Commande, l’Interpréteur, l’Itérateur, le Médiateur,
le Mémento, l’Observateur, l’État, la Stratégie, le Modèle de méthode, le Visiteur et
la Fonction de rappel.

62
En résumé :
- la Fabrique Abstraite fournit une interface, pour créer des familles d'objets
apparentés ou dépendants, sans avoir à spécifier leurs classes concrètes,
- l’Adaptateur convertit l'interface d'une classe en une autre qui est conforme aux attentes
du client. L'adaptateur permet à des classes de collaborer, classes qui n'auraient
pu le faire du fait d'interfaces incompatibles,
- le Pont découple une abstraction de son implémentation afin que les deux
éléments puissent être modifiés indépendamment l'un de l'autre,
- le Monteur dissocie, dans un objet complexe, sa construction de sa
représentation, de sorte que, le même procédé de construction puisse engendrer
des représentations différentes,
- la Chaîne de responsabilité évite le couplage de l'émetteur d'une requête avec ses
récepteurs, en donnant à plus d'un objet la possibilité d'entreprendre
la requête. Elle permet aussi de chaîner les objets récepteurs et fait passer la requête tout
au long de la chaîne, jusqu'à ce qu'un de ces objets récepteurs la traite,
- la Commande encapsule une requête comme un objet en autorisant le
paramétrage des clients à l’aide de cette requête,
- le Composite compose des objets en des structures arborescentes pour
représenter des hiérarchies. Il permet au client de traiter de la même et unique façon les
objets individuels de ces hiérarchies et leurs combinaisons,
- le Décorateur attache dynamiquement des fonctionnalités supplémentaires à un
objet. Les décorateurs fournissent ainsi une alternative souple à la dérivation,
- la Façade fournit une interface unifiée, à l'ensemble des interfaces d'un sous-
système. La façade fournit une interface de plus haut niveau, qui rend le sous-
système plus facile à utiliser,
- la Fabrique définit une interface pour la création d'un objet, tout en laissant à
des sous-classes le choix de la classe à instancier. Une fabrique permet de
déférer à des sous-classes les instanciations d'une classe,
- le Poids Mouche utilise une technique de partage qui permet la mise en uvre
efficace d'un grand nombre d'objets de fine granularité,
- l’Interpréteur définit, pour un langage donné, une représentation de sa
grammaire, en même temps qu'un interpréteur utilisant cette représentation en vue
d’interpréter les phrases du langage,
- l’Itérateur fournit un moyen d'accès séquentiel, aux éléments d'un agrégat
d'objets, sans mettre à découvert la représentation interne de celui-ci,
- le Médiateur définit un objet qui encapsule les modalités d'interaction d'un
certain ensemble d'objets. Le médiateur favorise le couplage faible en dispensant
les objets de se faire explicitement référence, et il permet donc de faire varier
indépendamment les relations d'interaction,
- le Mémento saisit l'état interne d'un objet et le transmet à l'extérieur, sans violer
l'encapsulation de cet objet. Ceci est fait dans le but de pouvoir restaurer ultérieurement
cet état,
- l’Observateur définit une interdépendance de type un à plusieurs, de façon telle
que, quand un objet change d'état, tous ceux qui en dépendant en soient notifiés
automatiquement et mis à jour,
- le Prototype spécifie les espèces d'objets à créer, en utilisant une instance de type
prototype, et crée de nouveaux objets par copies de ce prototype,

63
- la Procuration fournit à un tiers objet un mandataire ou un remplaçant, pour
contrôler l'accès à cet objet,
- le Singleton garantit qu'une classe n'a qu'une seule instance, et fournit à celle-ci,
un point d'accès de type global.
- l’État permet à un objet de modifier son comportement, quand son état interne
change. Tout se passera comme si l'objet avait changé de classe,
- la Stratégie définit une famille d'algorithmes, encapsule chacun d'entre eux, et
les rend interchangeables. Le modèle stratégie permet aux algorithmes d'évoluer
indépendamment des clients qui les utilisent,
- le Patron de méthode définit, dans une opération, le squelette d'un algorithme, en
en déléguant certaines étapes à des sous-classes. Le patron de méthode permet de
redéfinir par des sous-classes, certaines parties d'un algorithme, sans avoir à
modifier la structure de ce dernier,
- le Visiteur construit la représentation d'une opération applicable aux éléments d'une
structure d'objet. Il permet de définir une nouvelle opération, sans qu'il soit
nécessaire de modifier la classe des éléments sur lesquels elle agit,

64
E. ATELIERS - TRAVAUX PRATIQUES et EXERCICES

E1. ATELIER 1 (CODAGE)

A compléter pendant les travaux d’atelier

65
E2. ATELIER 2 (INTERFACE)

A compléter pendant les travaux d’atelier

66
E3. ATELIER 3 (DEVELOPPEMENT LOGIQUE)

A compléter pendant les travaux d’atelier

67
E4. ATELIER 4 (SUBDIVISION HIERARCHIQUE)

A compléter pendant les travaux d’atelier

68
E5. ATELIER 5 (MODELE EN CLASSES)

A compléter pendant les travaux d’atelier

69
CHAP. III : SPECIFICATIONS

A. DEFINITION ET RAPPEL

Toute action (instruction, programme, …) a sa raison d’être.


Cette raison d’être doit être exprimée à l’aide d’une SPECIFICATION correcte dans le fond et
dans la forme.
Une SPECIFICATION est un énoncé descriptif de la raison d’être d’une action qui
doit décrire, en utilisant des opérateurs et des expressions de haut niveau, l’objectif de cette
action, mais ne doit faire aucune référence à la manière de réaliser cet objectif.

Il est évident que chaque morceau d’une action décomposé se charge d’une partie de la
spécification de cette action.

De manière spécifique, soit s la spécification d’un programme p,


si a1, a2, ……., an sont les actions qui composent p, alors chaque ai se charge d’une partie si de
s tel que
Usi = s (union des si = s).

Il existe 2 types de spécifications : spécification informelle et spécification concrète.

La formulation des spécifications constitue une phase importante de développement qui permet
de :
- définir le contexte de chaque action,
- systématiser la conception d’une unité d’exécution en passant d’une expression informelle à
une expression formelle et plus complète de cette action.

B. SPECIFICATION INFORMELLE

Une spécification informelle est celle qui est utilisée pour exprimer l’objectif d’une action en
s’articulant essentiellement sur l’interface de cette action.
Elle fournit une vue générale de l’action et peut préciser de façon élémentaire certains
éléments de performances sur l’occupation de la mémoire, le temps d’exécution, …

C. SPECIFICATION CONCRETE

Une spécification concrète est la description de ce qu’accomplit une unité d’action en termes
concrets d’état initial, d’état d’exécution et d’état final de cette action.
Une spécification concrète fournit certains matériaux de base nécessaires pour la construction
des algorithmes.
La description de l’état initial, de l’état d’exécution et de l’état final d’une action se fait à l’aide
du contexte et des assertions.

Le contexte lié à l’état initial d’une action est formé de l’ensemble des objets visibles dans sa
situation de départ, càd situation dont l’action est censée partir.

70
Le contexte lié à l’état d’exécution d’une action est formé de l’ensemble des objets accessibles
au processeur pendant le déroulement de l’action.

Le contexte lié à l’état final d’une action est formé de l’ensemble des objets visibles dans sa
situation finale, situation générée par les résultats qu’elle produit.

Un objet est toute représentation d’information identifiable, sur laquelle le processeur peut
réaliser une action primitive (ex : un tableau, une base de données, un fichier, ...).

Une assertion est une relation algébrique ou logique qui décrit la situation dans laquelle doit se
trouver un contexte donné et peut donc être évaluée à «VRAI» ou à «FAUX».

Les assertions qui décrivent la situation d’un contexte lié à un état initial d’une action
s’appellent des PRE-CONDITIONS.

Les assertions qui décrivent la situation d’un contexte lié à un état d’exécution d’une action
s’appellent des INVARIANTS (ou hypothèses de récurrence).

Les assertions qui décrivent la situation d’un contexte lié à un état final d’une action
s’appellent POST-CONDITIONS.

La spécification concrète est la description de base dont les développeurs de l’action ont besoin
pour écrire les algorithmes correspondant à chaque composant de cette action.

Une spécification concrète est dite CONSISTANTE si ses pré-conditions sont vérifiables et si
ses post-conditions peuvent être réalisées, sinon elle est INCONSISTANTE.

Une spécification concrète consistante nécessite :


1. d’exclure tous les cas pour lesquels l’objectif induit par l’action n’est pas défini,
2. de dégager toutes les conséquences pouvant résulter des diverses valeurs autorisées des
objets,
3. de prévoir toutes les possibilités d’erreurs susceptibles d’être provoquées par
l’environnement (si des informations doivent provenir de l’environnement, les post-
conditions devraient prévoir des états de blocage).

C1. CONCEPTION DE SPECIFICATION CONCRETE

La spécification incarnant l’objectif d’une action a une influence certaine sur la


décomposition de cette action en sous-actions.
L’expression d’une spécification devrait être simple et tout porte à croire que cette
expression nécessite une certaine conception.

Lorsque l’expression de spécification d’une action devient LOURDE, LONGUE,


PEU LISIBLE et/ou COMPLEXE
(au point de vue de la nature des moyens et outils à déployer, de l’étendue de l’action
abstraite elle-même, …), il est préférable de décomposer cette spécification, sur base d’un
certain nombre de critères, en plusieurs sous-spécifications (pseudo-primitives) plus
simples qui réalisent chacune une sous-action abstraite.
Certains auteurs utilisent le vocable «paquetage » à la place de «pseudo-primitive».

71
Exemple : supposons que la spécification ci-dessous soit considérée comme peu lisible :
«calculer les racines d’une équation de second degré ax2 + bx + c = 0
en considérant les cas où :
- les 2 racines doivent être calculées,
- l’équation est dégénérée (càd a = 0) et qu’il faut calculer la racine unique,
- l’équation est triviale (càd c = 0) dans le cas où b = 0»,

alors, il peut être intéressant de décomposer l’action abstraite issue de cette


spécification, par exemple, en les 5 pseudo-primitives :
1. pseudo-primitive qui évalue le degré de l’équation,
2. pseudo-primitive qui calcule les 2 racines réelles,
3. pseudo-primitive qui calcule les 2 racines complexes,
4. pseudo-primitives qui calcule la racine unique,
5. pseudo-primitive qui vérifie la trivialité.

Une pseudo-primitive donnera donc finalement lieu à une partie d’algorithme qui utilise des
données globales de cet algorithme, elle est identifiée par une décomposition de
spécification.

Un module, contrairement à une pseudo-primitive, est une entité autonome pouvant être
analysée, codée ou comprise de façon isolée, son identification est associée à une
abstraction.

Toute spécification complexe de projets devrait donc être découpée en plusieurs sous-
Spécifications plus simples.
Une découpe pratique de projets est celle qui permet de dériver itérativement des sous-
ensembles quasi autonomes, appelés «application», «phase» et «tâche» en vue de maîtriser
sa mise en œuvre et son suivi.

Une application est une découpe primaire du projet qui isole des sous-ensembles où il
existe une grande AFFINITE sémantique entre les opérations fonctionnelles et une très
grande consommation et une grande vitesse de CIRCULATION des informations.

Une phase est une découpe d’application qui exige une exécution ININTERROMPUE
d’opérations et possède un TEMPS, un LIEU, et des RESSOURCES d’exécution
clairement IDENTIFIEES.
Deux phases peuvent être traitées en parallèle ou sous contraintes diverses de précédence,
de condition, d’exécution, d’inclusion, …

Une tâche est une découpe de phase qui accomplit généralement des fonctionnalités du
système opérant (système de base qui contient tous les comportements organisationnels
liés directement à la production technique correspondant à l’objet social d’une
organisation) et doit :
- fournir un résultat bien identifié et mesurable ;
- avoir une charge propre qui peut être évaluée.

72
Une tâche peut être identifiée en fonction des critères de :
- temps (répartition des éléments du système opérant dans le temps) ;
- structure du «produit livrable» ;
- regroupement par «produit livrable» ;
- responsabilité ;
- réutilisation ou similarité des opérations dans le système opérant ;
- regroupement par ressources ;
- cohérence technique (au point de vue de données propres à un sous-ensemble) ;
- cohérence d’organisation (au point de vue de «services», par ex.) ;
- cohérence fonctionnelle (au point de vue de gestion d’un «produit livrable»), …

Toute application, phase ou tâche doit être spécifiée.

C2. VALIDATION DE SPECIFICATION CONCRETE

A Chaque étape de décomposition ou de conception, toute spécification doit être validée.


Il y a 2 méthodes de validation :
1. validation externe, qui peut se faire par :
- explication (en langage naturel clair) de la spécification concrète,
- réalisation de maquette,
- rédaction des jeux de test,

2. validation interne, qui se focalise autour de la vérification de la consistance des


spécifications par des personnes différentes de celles qui ont écrit ces
spécifications.

Il faudra toutefois noter que, une spécification n’a toute sa signification que si l’action
y relative s’est exécutée normalement et s’est terminée.

C.3. SORTES DE SPECIFICATIONS CONCRETES

Il y a 3 sortes de spécifications concrètes :


- spécifications concrètes par règles,
- spécifications concrètes par assertions,
- spécifications de type abstrait.

a. Spécifications concrètes par règles


Ce sont des spécifications qui possèdent tous les éléments de base d’une spécification
concrète mais qui sont émises sous forme d’un ensemble NON STRUCTURE
des phrases descriptives en langue naturelle.

73
b. Spécifications concrètes par Assertions
Elle décrit plus méthodiquement et plus rigoureusement les arguments, les résultats et les
invariants d’une action abstraite à l’aide des assertions.
Une des formes rigoureuses est d’utiliser les matériaux du langage algébrique, du langage
logique et du langage des prédicats pour décrire :

- les arguments sous les termes :


E = liste d’objets (variables), ne venant pas de l’environnement, à l’entrée de l’action,
D = assertion exprimant les domaines de tous les objets (variables), ceux venant de
l’environnement et ceux ne venant pas de l’environnement,
PRE = assertion exprimant les pré-conditions,

- les éléments attachés à l’exécution sous le terme :


INV = assertion énonçant les propriétés attachées aux objets (variables) pendant toute
l’exécution de l’action,

- les résultats sous les termes :


S = liste d’objets (variables), non destinés à l’environnement, à la sortie de l’action,
I = assertion exprimant les images de tous les objets (variables), ceux destinés à
l’environnement et ceux non destinés à l’environnement,
POST = assertion exprimant les post-conditions.

Lorsque l’invariant est exprimé, il simplifie énormément la conception de l’algorithme


issue de l’action abstraite, en la rendant plus systématique et plus rigoureuse.
Dans certains cas, lorsque la spécification ne vise ni la conception, ni la maintenance de
l’algorithme, l’expression de l’invariant peut être ignorée.
Il faudra noter que l’on peut aussi exprimer une forme affaiblie de l’invariant.
Une forme affaiblie d’un invariant peut ne pas être vérifiée dans la situation initiale de
l’action.
Signalons que l’objectif réel de toutes les études sur l’invariant est la construction des
démonstrateurs et des constructeurs automatiques des programmes.

c. Spécifications concrètes de Type Abstrait


Un type abstrait est une classe qui regroupe les informations utilisables dans la
description ou le traitement d’un problème (action).
Pratiquement, il s’agit de trouver tout objet dont il est question dans le domaine, d’en
déterminer les primitives et de spécifier ce qui doit être spécifié à l’aide de ces
primitives.
Ainsi, par exemple le contexte lié à l’état initial de chaque primitive pourrait être vérifié
à l’aide d’autres primitives.

Un type abstrait est entièrement défini par :


- un NOM qui permet de désigner l’objet (l’ensemble abstrait qu’il représente),
ex : ENTIER,
- un ensemble de VALEURS autorisées pour les éléments de cet objet,
- un ensemble d’OPERATIONS associées à cet ensemble de valeurs,
- un ensemble d’AXIOMES caractérisant les propriétés de ces opérations.
TAD (Type Abstrait de Données) est un tel type abstrait.

74
Chaque opération est en fait une fonction partielle f d’un produit cartésien vers un autre
produit cartésien telle que :

f : X1 x X2 x X3 x ……x Xp Y1 x Y2 x …….x Yn

Ce couple de produits cartésiens est appelé SIGNATURE de l’opération.


A chaque opération sont donc associés une sémantique, un domaine de définition, et
un domaine (image).
Une spécification de type abstrait se base sur une description particulière des opérations
exécutables sur les structures de données visibles dans un domaine ou dans une action.

Dans le type abstrait, l’objet central est l’ensemble des structures de données, d’un
domaine ou d’une action, autour desquelles gravitent toutes les opérations.
Ces opérations sont considérées comme des primitives qui sont dotées chacune de sa
spécification.

D. ANOMALIES POSSIBLES

Une spécification peut comporter principalement 2 types d’anomalies :

1. Invalidité Externe : lorsqu’elle décrit une action réalisable mais qui ne correspond pas aux
besoins à résoudre (définis dans l’analyse fonctionnelle),

2. Invalidité Interne : lorsqu’elle présente des erreurs liées à sa formulation et qui peuvent
empêcher la réalisation des algorithmes demandés par l’action abstraite.
Cette invalidité est visible au travers des erreurs suivantes :
- spécification incomplète : lorsqu’aucun résultat n’est défini pour certaines valeurs possibles
des arguments,
- bruit : présence des éléments n’apportant aucune information utile,
- silence : absence d’éléments nécessaires pour caractériser l’action abstraite voulue,
- contradiction : présence des éléments définissant une caractéristique de manière
incompatible ;
- ambiguité : flou dû à l’utilisation d’ensembles non structurés de phrases pour décrire
l’action,
- référence en avant : élément de la phrase qui, pour définir une caractéristique du problème
ou de l’action, utilise des concepts n’ayant pas encore été définis.
Cela est généralement dû à un mauvais repérage des concepts
fondamentaux liés au problème ou à l’action,
- surspécification : élément qui ne définit pas une caractéristique du problème ou de l’action
mais d’une solution possible.
La surspecification est très dangereuse car elle rend la validation
impossible ou inexacte car des éléments fondamentaux du problème
n’ont pas été signifiés dans la structure.

Les conséquences de ces anomalies sur les spécifications sont très coûteuses lorsqu’elles sont
découvertes tard dans le processus de développement.
Il est alors indispensable de mettre en œuvre des méthodes systématiques de validation
des spécifications (cfr chap. IV) en vue d’identifier ces anomalies dès la phase de
spécification.

75
Remarques :

En cours de démonstrations ou de constructions de programmes, on est généralement amené à


appliquer des manipulations algébriques d’assertions et des règles d’inférence auxiliaires permettant
de simplifier les dérivations et leur lecture.

Les règles et les manipulations les plus courantes sont :


1. si {Q} s {R} Λ Q  Q’ Λ R  R’ alors {Q’} s {R’}
2. - (s,false) = + (s,false) = false
3. - (s,true) = + (s,true) = true
4. -/+ (s, R1 Λ R2 Λ …… Λ Rn) = -/+ (s, R1) Λ -/+ (s, R2) Λ …… Λ -/+ (s, Rn)
5. -/+ (s, R1 V R2 V …… V Rn) = -/+ (s, R1) V -/+ (s, R2) V …… V -/+ (s, Rn)
6. x  y ≡ non (x Λ non (y)) ≡ non (x) V y,
on voit que cette implication est vraie dans les cas suivants :
x = faux et y = faux,
x = faux et y = vrai,
x = vrai et y = vrai et
elle est fausse lorsque x = vrai et y = faux.
Quand x est faux, on ne sait rien affirmer sur l’assertion y,
7. ( ∀ x) ( ∀ y) f(x,y) ( ∀ y) ( ∀ x) f(x,y)
8. ( ∃ x) ( ∃ y) f(x,y) ( ∃ y) ( ∃ x) f(x,y)
9. ( ∀ x) ( ∃ y) f(x,y) ≠ ( ∃ y) ( ∀ x) f(x,y),
par exemple : ( ∀ n) ( ∃ m) m>n ≠ ( ∃ m) ( ∀ n) m>n, (m, n € N),
la proposition à gauche de ≠ est correcte car elle affirme que pour tout n, il existe un
entier m qui soit supérieur à ce n,
la proposition à droite de ≠ n’est pas correcte car elle dit qu’il existe un entier m qui soit
soit supérieur à n’importe quel autre entier n,
10. ( ∀ x) (f(x) Λ g(x)) ( ∀ x) f(x) Λ ( ∀ x) g(x)
11. ( ∃ x) (f(x) Λ g(x)) ( ∃ x) f(x) Λ ( ∃ x) g(x)
12. ( ∀ x) (f(x) V g(x)) ( ∀ x) f(x) V ( ∀ x) g(x)
13. ( ∃ x) (f(x) V g(x)) ( ∃ x) f(x) V ( ∃ x) g(x)
14. ( ∀ x) ( ∀ y) f(x,y) ( ∃ x) ( ∃ y) f(x,y)
15. ( ∀ x) ( ∀ y) f(x,y) ( ∃ x) ( ∀ y) f(x,y)
16. ( ∀ x) ( ∃ y) f(x,y) ( ∃ x) ( ∃ y) f(x,y)
17. ( ∃ x) ( ∀ y) f(x,y) ( ∀ y) ( ∃ x) f(x,y)
18. ( ∀ x) (f(x) Λ g(x)) ( ∃ x) f(x) Λ ( ∃ x) g(x)
19. ( ∀ x) (f(x) V g(x)) ( ∃ x) f(x) V ( ∃ x) g(x)

20. ( ∀ x) ( ∀ y) f(x,y) ( ∀ x) ( ∀ y) f(x,y)

21. ( ∀ x) ( ∀ y) f(x,y) ( ∃ x) ( ∃ y) f(x,y)

22. ( ∃ x) ( ∃ y) f(x,y) ( ∀ y) ( ∀ x) f(x,y)

23. ( ∃ x) ( ∃ y) f(x,y) ( ∃ y) ( ∃ x) f(x,y)

76
24. Il ne faut pas confondre la négation de toute une assertion à la négation des phrases de
cette assertion individuellement :

( ∃ x) f(x) ≠ ( ∃ x) f(x) ,

car ( ∃ x) f(x) = ( ∀ x) f(x)

( ∃ x) f(x) = ( ∀ x) f(x)

77
CHAP. IV : ETAPES FONDAMENTALES DE DEVELOPPEMENT

A. STRUCTURATION HIERARCHIQUE

Structurer hiérarchiquement (ou hiérarchiser) un système ou tout autre problème est l’objectif
principal de toutes les méthodologies de développement des logiciels lors de la conception
globale.
La structuration hiérarchique permet, sur base des relations logiques R, de produire des niveaux
hiérarchiques et ordonnés entre les composants d’une architecture logicielle.
Un composant est, dans cette structuration, tout objet «métier» ou autre qui peut générer 1 ou
plusieurs modules ou codes.
Cette étape fournit un dossier de conception (cahier de charges technique) comprenant une partie
destinée au client (présentation de la solution) et une partie destinée aux développeurs
(conception technique).

A1. RELATION DE STRUCTURATION ET INTERET

Soient 2 composants A et B au sein d’un système hiérarchisé,


i, le niveau hiérarchique du composant A, et
j, le niveau hiérarchique du composant B,
si R(A,B) alors i ≠ j (typiquement i < j).

Plus généralement, soit un système S qui contient les composants M1, M2, …, Mn, càd
S = {M1, M2, …, Mn}, une relation R est une partie de S x S.
On écrit R(Mi,Mj) ou Mi R Mj si Mi et Mj sont liés par la relation R qui a comme
extrémité initiale Mi et comme extrémité finale Mj.
La clôture transitive de la relation R, notée R+, est telle Mi R+ Mj si :
- Mi R Mj ou
- il existe Mk tel que Mi R Mk et Mk R+ Mj

La hiérarchisation d’un système est fonction de la nature et de la puissance de R.

La puissance d’une relation est liée à son potentiel de ne jamais induire des cycles entre les
composants à organiser.

L’intérêt de structurer hiérarchiquement (ou de hiérarchiser) un système ou tout autre


problème est de ressortir une structure fondamentale (typiquement arborescente) à
partir de l’ensemble de composants de ce système ou de ce problème en vue de contrôler et
de maîtriser essentiellement l’échange de ses informations.

La structuration hiérarchique est l’essence de la cybernétique et de la construction des


machines complexes.

78
Notons que :
- les niveaux hiérarchiques d’une hiérarchie n’ont pas nécessairement une sémantique
immédiate et clairement définie, surtout si ses composants sont générés par plusieurs
relations R,
- dans l’étape de la structuration modulaire, un composant pourra donner lieu à 1 ou plusieurs
modules ou faire partie d’un autre module, ce qui pourrait induire d’autres relations R.

Quelques relations (hiérarchies) parmi les plus remarquables :


Est primitive de, Est partie de, Envoie un message donné à, Utilise, Appelle,
Précède, Alloue des ressources à, Est composé de, Vient de, …

A2. CRITERES PRINCIPAUX D’UNE RELATION DE Structuration HIERARCHIQUE

Au sein d’un système qui a été structuré hiérarchiquement, si l’on a R(A,B) alors :
1. le composant A doit être nettement plus simple (surtout au point de vue conception
et spécification) du fait d’être en relation avec le composant B,
2. le composant B ne doit pas devenir beaucoup plus compliqué (surtout au point de vue
conception et spécification) du fait d’être en relation avec le composant A,
du fait d’être en relation avec le composant B,
3. il peut exister un sous-ensemble fonctionnellement utile de composants qui contient B et
non A,
4. il ne devrait pas exister un sous-ensemble fonctionnellement utile de composants qui
contiendrait A sans contenir B.

Les bénéfices immédiats de ces 4 critères sont :


- conceptions aisées (celui qui conçoit A n’est pas obligé de connaître le design de B),
- simplification des spécifications,
- minimalisation automatique du degré de couplage entre composants,
- validation et maintenance simplifiées et rapides,
- élimination automatique de certaines redondances dans la mise en œuvre.

Les relations qui répondent correctement à ces 4 critères sont naturellement celles qui font
apparaître des structures arborescentes ou des sous-ensembles parmi les composants du
système.
Il existe donc des relations naturellement fortes (celles qui privilégient des éléments de
gestion des notions stables telles que graphes, arbres, groupes, listes, queues, tableaux, …) et
des relations qui sont naturellement faibles (celles qui induisent des cycles dans leur
structuration),

Si la relation R est telle que R(A,B) et R(B,A) pour un certain nombre de composants A
et B du système alors R ne devrait être utilisée que très prudemment pour structurer un
système,
dans ce cas, il peut être possible de décomposer un des composants (B, par exemple) en
composants Bi et Bj de façon telle que cette relation R devienne hiérarchique pour avoir
R(Bi,A) et R(A, Bj) et non R(A,Bi) ni R(Bj,A).

79
A3. RELATION «UTILISE»

La relation «UTILISE» est une relation de structuration hiérarchique qui est naturellement
forte qui permet de décomposer un système en des sous-ensembles (couches) disjoints sur
base des «services» à fournir à d’autres composants du système.

Ainsi, si UTILISE(A,B) alors :


- le composant A demande un service au composant B (qui est plus outillé pour rendre
ce service),
- la spécification du composant A n’est pas valide si la spécification du composant B
n’est pas respectée.

La relation «UTILISE» veille donc à une application stricte du 4ème critère et ne doit pas
être confondue avec la relation «APPELLE».

Exemple : Soient FACTURATION = composant de préparation de facture pour l’édition,


AJUSTEMENT = composant pour ajuster le montant de la facture en
fonction des remises, bonis, avances et crédits spéciaux
accordés au client,
si R(FACTURATION,AJUSTEMENT), alors visiblement le fonctionnement
correct de FACTURATION ne dépend pas du fonctionnement correct de
AJUSTEMENT (FACTURATION peut ne pas recourir aux services de
AJUSTEMENT et
AJUSTEMENT peut ajouter ou retrancher une somme
inconsidérée sans donner lieu à un mauvais fonctionnement de
FACTURATION).
Donc R signifierait plutôt «APPELLE».

Exemple : Soient CODAGE = composant qui, en fonction d’une langue donnée, formule
(corrige) un code binaire en y insérant des «bits additifs»,
INSERTION = composant qui interprète les mots issus du code binaire en
vue de les insérer dans un texte donné,
si R(INSERTION,CODAGE), alors visiblement le fonctionnement
correct de INSERTION dépend totalement du fonctionnement correct de
CODAGE (INSERTION a toujours besoin de CODAGE,
un mauvais travail d’insertion dans CODAGE donne lieu à un
mauvais fonctionnement de INSERTION, qui donc ne pourra pas
respecter sa spécification).
Donc R pourrait valablement signifier «UTILISE».

Exemple : Les composants dans les 7 niveaux de OSI et les niveaux d’abstraction des
architectures des systèmes d’exploitation sont regroupés sur base de la relation
«UTILISE».

Exemple : On peut écrire, en faisant abstraction de certains éléments :


UTILISE (niveau logique de Mérise, niveau conceptuel de Mérise), car en Mérise,
le niveau logique a toujours besoin du niveau conceptuel et un mauvais modèle
conceptuel révélera un mauvais modèle logique.

80
B. STRUCTURATION MODULAIRE

B1. MODULARISATION

L’étape de structuration modulaire procède à une reformulation éventuelle des composants de


l’architecture, soit en les regroupant ou/et en les éclatant en fonction des spécifications, des
objectifs de gestion (métiers) et des abstractions existant dans le domaine ou en dehors de ce
domaine afin d’éviter d’aboutir à une DECOUPE FONCTIONNELLE du système.

Il est vital pour les développeurs de pénétrer tous les aspects des spécifications informelles des
fonctionnalités et des traitements du domaine afin d’en dégager des pseudo-primitives ou des
actions principales.
Ces pseudo-primitives et actions principales permettront de fournir une base pour cette
reformulation des composants.

Rappelons qu’une spécification informelle décrit une vision de l’utilisateur et sert simplement
à caractériser l’interface d’une fonctionnalité en termes d’entrées et de sorties.

Une DECOUPE FONCTIONNELLE de système fournit généralement un grand ou un


mauvais degré de couplage entre les composants, c’est ce que l’on obtient par exemple en
Mérise, si le concepteur fait correspondre d’office un module à chaque phase du MOT
(Modèle Organisationnel des Traitements).

Cette reformulation générera d’autres relations sur l’architecture et fournira finalement les
modules du système.
Chaque module devra être caractérisé par :
- sa spécification concrète, qui permettre aux développeurs de le développer et de le valider,
- son mode de réalisation, qui définit en fait son implémentation physique (client, serveur,
module centralisé, procédure, routine, classe, unité de compilation séparée, …) et les
performances spécifiques de son exécution (complexité en temps, en espace, degré de
parallélisme de ses fonctionnalités, …).

Un module peut représenter une classe d’abstraction d’ACTION ou d’OBJET.

Les architectures modulaires «orientées objets» produisent des modules d’objet car elles se
basent essentiellement sur une abstraction d’objets. Elles répartissent les données du domaine
dans des modules qui sont quasiment disjoints et possèdent ainsi une prédisposition
naturelle à générer des modules faiblement couplés et qui sont fortement réutilisables.

Les architectures modulaires «orientées actions» produisent des modules d’action car elles se
basent essentiellement sur la réalisation d’un ensemble lié d’actions complexes sur les données
du domaine.
Cette abstraction correspond à un ensemble cohérent de transformations fonctionnelles
applicables sur plusieurs groupes de données.
L’abstraction d’action possède généralement des fortes chances de conduire, si l’on ne prête
pas une attention soutenue, à des modules fortement couplés.
Les architectures «orientées actions» possèdent une testabilité beaucoup plus grande que les
architectures «orientées objets».

81
B2. EXEMPLE SIMPLE

Illustrons quelques traits de ces architectures au travers de l’exemple simple suivant :

Soit A, un ensemble (non trié) des commandes non payées.


Ecrivons une architecture «orientée objets» et une architecture «orientée actions» dont le rôle
est d’analyser (de déterminer) si une commande n’est pas encore payée.

En architecture «orientée actions», on peut aboutir aux modules suivants :


TRI (A) : module qui trie l’ensemble A en une structure triée A’ des commandes non payées
en ordre croissant, par exemple,
RECHERCHE (x,A) : module qui renvoie «vrai» si x se trouve dans A.

On aboutirait ainsi, par exemple, à l’architecture suivante :

RECHERCHE

utilise

TRI

On peut remarquer que l’ensemble A et sa structure doivent être connues dans ces 2
modules (toute modification de A oblige de revisiter ces 2 modules),

L’architecture «orientée objet» peut fournir la conception globale :

Numéro
Date, …

Trouvé()
Total()
Ajout(), …

Un composant client qui cherche à savoir si la commande x est non payée enverra un
message à A pour déclencher la méthode «Trouvé(x)» et n’a pas besoin de connaître les
détails de structure ni d’implémentation de A (on peut modifier A sans toucher aux modules
«client»).

82
C. DEVELOPPEMENT DE MODULE

C1. METHODES DE CONCEPTION

La conception ou le développement d’un module, càd la traduction méthodique de ses


spécifications concrètes en algorithmes, est un processus RECURSIF et ITERATIF
comprenant :
- l’étude et le choix de représentation interne des données,
- la production des éléments algorithmiques pour chaque action du module.

Pour la production des éléments algorithmiques correspondant à chaque action du module,


toutes les méthodes BOTTOM-UP (exprimée essentiellement à l’aide de l’ordinogramme)
sont à éviter, car elles ont une prédisposition naturelle à produire de la confusion.

Les méthodes BOTTOM-UP contiennent des bons outils d’analyse d’exécutions


particulières d’un programme donné, mais elles ne devraient pas être utilisées pour
concevoir des algorithmes et des programmes.

Les méthodes fiables et efficaces pour la production des éléments algorithmiques relatifs à
chaque action du module sont principalement les méthodes TOP-DOWN et les méthodes
guidées par la RECURRENCE.
Ces 2 méthodes ont fourni des briques de base aux autres méthodes de conception qui n’ont
pas été citées dans ce cours.

C11. Méthodes de Conception TOP-DOWN (Descendantes)

La plupart des méthodes de conception TOP-DOWN ont été à la base des éléments
constitutifs des méthodologies citées ci-avant.
Elles ont le grand mérite de répondre aux spécifications concrètes d’un module par
raffinements successifs en partant des concepts très généraux de conception vers des
concepts détaillés et en se basant sur des critères plus ou moins solides de raffinement à
chaque niveau de raffinement (ex : méthodes LCP, Warnier, …).

C12. Méthodes de Conception guidées par la RECURRENCE

Les méthodes de conception guidées par la récurrence sont généralement utilisées pour
développer des modules qui manipulent des données contenues dans des structures
stables (tableaux, piles, listes, files d’attente, graphes, arbres, …) et s’inspirent des
concepts logiques du développement démonstratif.

La base de ces méthodes est de :


- supposer que le problème posé dans le module est déjà résolu pour un certain nombre
de données à manipuler,
- trouver une transformation simple, pour les données restant à manipuler, qui permettra
d’exprimer une situation générale (invariant) et de converger vers la situation finale
désirée.

83
Lorsqu’il s’avère que toutes les données (ou une grande partie) doivent être visitées
selon une séquence régulière (grâce aux types de parcours attachés naturellement
à ces structures stables), la base de ces méthodes fournit l’algorithmique correspondant
au cœur du module auquel il faudra établir les conditions initiales et finales.
La base de ces méthodes peut s’étendre à des données qui n’ont rien à voir directement
avec des structures stables mais qui, dans la manipulation, font appel aux parcours
natifs de ces structures.

C2. OUTILS

Le développement algorithmique d’un module doit être décrit ou représenté.


Les outils de description peuvent être en langues naturelles, en pseudo-langages, en langages
algébriques, en langages graphiques, … et/ou incorporer des tables de décision, mais ils
doivent :
- respecter les concepts méthodologiques vus au niveau de code,
- exprimer clairement les mécanismes fondamentaux de contrôle de flux et d’opérations
élémentaires sans détails de codage,
- utiliser une syntaxe et une sémantique transparentes et ne privilégier que les mécanismes
qui rendent un code communicable.

Exemple d’un outil graphique efficace, celui qui utilise les 4 figures suivantes :

Mécanisme d’exécution séquentielle de la suite A


d’actions,
A

?p Exécution conditionnelle des mécanismes A si le


p -p prédicat p est vrai ou des mécanismes B si le prédicat
p est faux,
A B

p Exécution répétitive des mécanismes A jusqu’à non p,

Exécution répétitive des mécanismes A jusqu’à p


A

84
Petite mise en pratique. Explicitons la spécification de l’algorithme suivant :

i ← 1
j ← 1

i ≤ n

j ≤ m

i = j
vrai faux

a [i,j] ← 0 a[i,j] ← a[i,j] - 1

j ← j +1

i← i + 1
j← 1

85
D. CONCEPTION DE L’ARCHITECTURE PHYSIQUE

D1. INTRODUCTION

La tâche principale de cette étape est de conditionner les algorithmes et les programmes pour
qu’ils puissent tournent sur des machines réelles insérées dans une architecture physique
déterminée. Elle doit donc concrètement mettre en œuvre le mode de réalisation de chaque
élément constitutif de l’application. Donc, le cycle de vie d’une application devrait
explicitement énoncer un modèle conceptuel (plus stable), un modèle logique (architecture
logicielle qui est moins stable que le modèle conceptuel) et un modèle physique (modèle le
plus instable).

Il est toutefois acceptable de concevoir un modèle logique d’application en fonction d’un


modèle physique donné, mais il n’est pas correct de concevoir le modèle conceptuel d’une
application en fonction d’une architecture physique donné.

En tenant compte principalement de l’environnement d’outils-logiciels (plates-formes de


développement, types de gestionnaires de données et de fichiers, …), il faudra :
- réaliser l’entité physique correspondant au mode de réalisation de chaque programme en
adaptant éventuellement les algorithmes (un module pourrait, par exemple, conduire à
l’implémentation de plus d’une entité physique si l’on sépare les aspects de présentation
de ceux de l’accès aux données),
- coder les algorithmes en programmes.

L’ensemble des entités physiques forment l’architecture physique de l’application.

D2. DISTRIBUTION

Les différents composants et modules peuvent tourner dans une architecture physique de
type :
- égal-à-égal : qui implémente le protocole du même nom et qui permet à un composant de
s’adresser à un autre pour lui demander d’effectuer une tâche ou pour lui
communiquer un résultat,
- système centralisé 1 niveau (ou 1/3) : où tout le système est installé sur 1 ou
plusieurs machines centrales (mainframes) sans réelle distribution car ces
machines centrales s’occupent de la gestion des données et des
applications.
Un des grands avantages de cette architecture est de faciliter la gestion de
certains composants auxiliaires (sécurité, …),
- système client/serveur 2 niveaux (ou 2/3) : où tout le système est découpé en couches en
permettant que le serveur gère les données et les machines clientes s’occupent
de la présentation et de la logique des traitements.
Ici, il y a une réelle distribution car les machines clientes adressent des
requêtes au serveur et reçoivent des résultats de la part de ce serveur,
- système client/serveur 3 niveaux (ou 3/3) : où tout le système est découpé en couches en
permettant qu’un serveur de données gère les données, un serveur
d’application gère la logique des traitements et les machines clientes
s’occupent uniquement de la présentation.

86
Ici, il y a une réelle distribution car les machines clientes adressent des
requêtes au serveur d’application (qui, à son tour s’adresse au serveur de
données) et reçoivent des résultats de la part de ce serveur d’application.
Ex : machine cliente = navigateur Web,
serveur d’application = serveur HTTP,
serveur de données = serveur Oracle,

- système client/serveur n niveaux (ou n/3) : qui est en fait une généralisation du
système client/serveur 3/3 qui accepte plusieurs serveurs de données pour
gérer les données de l’application et/ou plusieurs serveurs d’application
pour prendre en charge la logique des traitements.
Les machines clientes ne s’occupent uniquement que de la présentation.

D3. GESTION DE SOCKETS

Actuellement, les applications et les systèmes sont de plus en plus distribués sur des réseaux
(Internet ou autres), il est important de cerner le principe de base qui constitue le fondement
de la programmation «réseau», celui de la gestion de «socket».

Une application web est une application livrée aux utilisateurs à partir d'un serveur web par
un réseau tel que l’Internet ou l’Intranet.

Les applications web deviennent de plus en plus populaires grâce à :


- à l'omniprésence du Navigateur Web comme client (client léger),
- à la capacité de gérer et de maintenir des applications sans distribuer ni installer le
logiciel correspondant sur potentiellement des milliers de machines clientes.

D31. Généralités

Une SOCKET est une interface logicielle qui permet la communication entre un
système et un réseau. Elle est l'interface de programmation réseau la plus courante.
Des processus peuvent se connecter à une socket pour y envoyer des données ou pour
en recevoir, on peut ainsi, à l’aide de cette interface, développer toute application de
réseau ou de communication en temps réel.

Une socket est formée du nom de l'hôte (client) et d’un numéro de port distant.

Il suffit que les processus qui désirent se communiquer à l’aide de sockets adoptent le
même protocole de communication.
A l’aide de cet outil de communication, sous l’architecture client/serveur, le client qui
est le processus qui décide à un certain moment d’envoyer des données au serveur, doit
posséder une socket,
le serveur qui est le processus passif qui reste à l’écoute de toute demande de
connexion de client doit aussi posséder une socket.

87
D32. Sortes de Sockets

On distingue généralement 4 types de sockets :


- la socket stream qui permet une communication bidirectionnelle, sûre, séquencée
et un flux de données sans duplication pouvant entraîner une fragmentation
des paquets transmis,
- la socket datagram qui permet une communication bidirectionnelle qui n'est pas
séquencée, pas sûre, et peut éventuellement entraîner une duplication des
données,
- la socket raw qui permet aux utilisateurs d'accéder à des protocoles de
communication différents en même temps et qui permet d'avoir accès à des données
"brutes" et sont utilisées par exemple pour analyser le trafic d'un réseau,
- la socket sequenced packet ressemble à une socket stream mais n'utilise
pas de fragmentations de paquets.

88
E. VALIDATION

E1. INTRODUCTION

Avant de reproduire un système dans un environnement de production, il doit être validé.


Les 2 approches de validation d’une unité d’exécution sont le TEST et la
DEMONSTRATION.
Lorsque l’on teste un programme, on essaie, après conception, d’y découvrir un maximum
d’erreurs afin de pouvoir les corriger. Le test est donc un processus destructif.
La démonstration est un outil efficace et implacable, qui permet finalement de livrer un
programme ne contenant aucune erreur (étant donné sa spécification).
De manière idéale, une démonstration devrait faire partie intégrante du processus de
développement.

E2. TEST

A. INTERETS et LIMITES
Dans le processus de test, on découvre généralement des symptômes et pas les causes.
L’avantage du processus de test réside dans la visualisation directe du comportement des
programmes dans un environnement physique réel.

B. PRINCIPES
Il s’agit des principes qui permettent de formaliser le processus de test, essentiellement à
l’aide des notions de critères de couverture, de l’indépendance et de plans de test :

- Critère de couverture
Lorsqu’il est impossible de procéder à des tests exhaustifs (c’est généralement le cas),
un critère de couverture est utile pour :
a. diminuer le nombre de tests à faire à l’aide de jeux de test, et
b. augmenter le nombre de cas dans l’espace de la couverture.
Un critère de couverture est donc une règle qui sélectionne des objets (variables,
valeurs, structures, …) à tester par sous-ensembles sur base d’un certain nombre de
caractéristiques,

- Indépendance
L’indépendance doit garantir que les personnes qui ont conçu un programme ne doivent
pas faire partie de l’équipe des personnes désignées pour tester ce programme,

- Plan de test
Un plan de test est une organisation du processus de test et qui détermine une stratégie
particulière composée de :
a. l’objectif du test (ex : suivre une combinaison des valeurs dans un programme)
b. parties du programme concernées,
c. critères de couverture retenus,
d. l’ensemble des couples {(données du test),(résultats attendus)}.
Un plan de test devrait être établi pour chaque étape cruciale du cycle de vie en vue de
découvrir des erreurs de spécification, des erreurs de conception, des erreurs de codage
et des erreurs d’intégration.

89
C. TYPOLOGIE DES JEUX DE TEST
Il existe 3 types de jeux de test

a. Jeux de test «Black Box» («Boîte Noire ou Fonctionnels)


Ce sont de jeux de test qui sont déduits directement des spécifications (informelles ou
concrètes) sans connaissance des algorithmes correspondants.
Leur objectif principal est de détecter et d’analyser des erreurs de spécifications.
Le critère de couverture principal est la génération de classes d’équivalence
(réflexivité, transitivité et symétrie), à intersection vide 2 à 2, sur les arguments :
classes de valeurs acceptables Ai et classes de valeurs non-acceptables Xi qui couvrent
totalement tout le domaine des arguments.
La relation au sein de chaque classe doit être clairement déterminée
(«est proche de valeurs frontières du domaine d’un argument que»,
«est dans la même plage que», …).

La notion de classe d’équivalence permet de regrouper des cas redondants en sous-


ensembles, d’éliminer des cas impossibles et de ne garder que des combinaisons
cohérentes.
Plus systématiquement, il faut :
- identifier, pour chaque argument ou combinaison utile d’arguments, des classes Ai et
Xi,
- construire des jeux de test pour autant des classes Ai que possible,
- construire des jeux de test pour une seule classe Xi à la fois, afin d’isoler et de tester
individuellement chaque cas non-acceptable,
- procéder au test.

b. Jeux de test «White Box» («Boîte Blanche» ou Structurels)


Ce sont de jeux de test qui sont déduits directement des algorithmes associés à un
programme.
Leur objectif principal est de détecter des erreurs de codage essentiellement.
Dans la plupart de ces jeux de test, les algorithmes doivent être transformés sous forme
d’ordinogrammes ou de graphes de contrôle.
Les critères de couverture les plus utilisés dans ces jeux de test sont :
- la couverture de chaque instruction (C0),
- la couverture de toutes les conditions (C1),
- la couverture de chemin élémentaire,
- la couverture de chemin non-élémentaire,
- la couverture et expansion des sous-domaines,
- le critère de test de flots de données,
- le critère de test aléatoire,
- le critère de test aux limites,
- le critère de circuit prédicatif,
- le critère de circuit aplati,
- le critère de test Méthode-Message (MM),
- le critère de test par paire de fonctions.
Le critère de test Méthode-Message (MM) et par paire de fonctions sont plus spécifi-
ques pour les logiciels dans lesquels les interactions entre les méthodes ne sont pas
explicitement mentionnées et pour lesquels l’expression sous forme d’ordinogrammes
ou de graphes de contrôle n’est pas adaptée (OOD, essentiellement).

90
c. Jeux de test d’intégration
Ce sont des jeux de test qui sont déduits de l’architecture physique des programmes en
vue de tester l’intercommunication des diverses entités physiques et leur enchaînement.
Ces test sont faits après que chaque programme de l’architecture ait déjà été testé
individuellement (test unitaire).
Soit un programme P, alors tous les programme Si physiquement invoqués par P sont
appelés programmes «souches» de P, et tous les programmes Ci qui invoquent P sont
appelés programmes «conducteurs» de P.

Par exemple, dans l’architecture physique :

B C D

A est le programme «conducteur» de C, et F son programme «souche».

Il existe 2 stratégies pour la conduite de tests d’intégration :


- test d’intégration non-incrémentale,
Ce test procède par simulation des résultats de tous les programmes «souches» du
programme à tester.
Pour l’architecture ci-dessus, on aura 3 tests d’intégration non-incrémentale :
pour A, qui a 3 souches, simulation des résultats de B,
simulation des résultats de C,
simulation des résultats de D,
pour B, aucune souche, pas de test d’intégration,
pour C, qui a 1 souche, simulation des résultats de F,
pour D, aucune souche, pas de test d’intégration,
pour F, qui a 1 souche, simulation des résultats de G,
pour G, aucune souche, pas de test d’intégration.

91
- test d’intégration incrémentale,
Dans ce test, un programme est testé en combinaison avec tous les autres qui ont déjà
été testés et intégrés.
Pratiquement, pour éviter de simuler les résultats des programmes «souches», le test
d’intégration incrémentale commence par intégrer les feuilles.
Pour l’architecture ci-dessus, on aura 6 tests d’intégration incrémentale :
F et G,
C, F et G,
A et B,
A, C, F et G,
A et D,
A, B, C, F, G et D

D. NOTIONS TECHNIQUES DE BASE

D1. Couverture de chemin

Tout programme peut se mettre sous forme d’ordinogramme ou de graphe de


contrôle.
Un ordinogramme est un graphe orienté qui exprime le flux de contrôle d’un
programme à l’aide d’un enchaînement des nœuds d’exécution et de décision
essentiellement, avec des retours possibles en arrière.
Un graphe de contrôle est un ordinogramme sans retour en arrière et dont chaque
nœud peut contenir un ensemble d’instructions.
Un ordinogramme ou un graphe de contrôle est constitué de chemins dont la forme
générale est :

N0

arc 0

N1

arc 1

N2 Ni = noeud d’exécution ou de
décision
arc 2

.
.
.

arc p-1

Np

92
Un chemin est une suite cohérente d’arcs et de nœuds - d’un programme - dont
l’origine est un nœud d’entrée de ce programme et la fin un nœud de sortie de ce
même programme.

Un algorithme pourvu de n conditions contient, sous forme d’ordinogramme, au plus


2n chemins (!!! les structures répétitives cachent des conditions qu’il faut ressortir).

L’idée de base au sein de la plupart des tests qui utilisent le critère de chemin
est : 1. de se fixer un chemin au sein du programme à tester,
2. de trouver un jeu de test qui pourrait parcourir le chemin fixé.

Chaque arc i du chemin porte, comme on l’a vu ci-avant, un prédicat appelé


«prédicat arc» (généré par l’action exécutée par le nœud père i et par d’autres
actions antérieures) qui permettra que cet arc i soit traversé ou non.

Un nœud de décision (sélection) ne modifie aucune variable ou objet du contexte,


mais ses 2 voies de sortie ne peuvent pas coexister simultanément dans un chemin,
donc, on ne sait pas dire d’avance si un prédicat, au sortir d’un nœud de décision sera
vrai ou faux;
mais, le prédicat collé à un arc au sortir d’un nœud d’exécution sera toujours vrai, car
c’est la seule voie et le processeur ne pourra que sortir par cet arc.

Un critère de chemin élémentaire est un critère qui considère que chaque boucle
itérative du chemin visé n’est exécutée qu’une seule fois.
Un critère de chemin non-élémentaire est un critère qui tient compte du nombre réel
de fois que toute boucle est exécutée dans le chemin visé.

Le «prédicat chemin» d’un chemin est la conjonction des «prédicats arcs» des arcs qui
composent ce chemin.

Pour simplifier l’expression S d’un «prédicat chemin», on peut y omettre tous les
«prédicats arcs» associés aux nœuds d’exécution.
La résolution de S permettra de trouver un jeu de test pour le chemin visé.

Il peut être très délicat de déterminer si un chemin est impossible à parcourir ou s’il
est seulement compliqué car la résolution de S, par des méthodes systématiques
(comme celles de résolution des équations) ou empiriques (de proche en proche ou en
faisant des hypothèses acceptables) peut conduire à des indéterminations.
Ces indéterminations sont dues à la présence du signe d’affectation (←).
Certaines de ces indéterminations peuvent être levées en recommençant les calculs de
prédicats et en exécutant les boucles des chemins élémentaires un nombre de fois > 1.

93
Exemple : Exécutez un test de couverture de tous les chemins à partir d’un
graphe de contrôle de l’algorithme suivant :

début
lire a, b, c
x←5
y←7
si (a>b et b>c) alors a←a+1
x←x+6
si (a=10 ou b>20) alors b←b+1
x←y+4

si (a<10 ou c=20) alors b←b+2


y←4
a←a+b+1
y←x+y

si (a>5 ou c<10) alors b ← c + 5


x←x+1
écrire x, y
fin

94
Un graphe de contrôle possible est :

A
lire a, b, c
x ← 5; y ← 7
si (a>b et b>c)
vrai

faux a←a+1 B
x←x +6
si (a=10 ou b>20)
b←b+1 C
faux x←y+4

si (a<10 ou c=20)
D

b←b+2 E
F y←4
a←a+b+1
y←x+y

G
si (a>5 ou c<10)

b←c+5
H
x←x+1
I
écrire x, y

Ce graphe de contrôle possible contient 4 conditions :


(1) si (a>b et b>c)
(2) si (a=10 ou b>20)
(3) si (a<10 ou c=20)
(4) si (a>5 ou c<10)

95
Les chemins sont :
1. chemin AGI, condition conjuguée : FxxF (càd la condition 1 doit être fausse,
la condition 2, quelconque,
la condition 3, quelconque et
la condition 4, fausse),

expression de jeu de test : non (a>b et b>c) ET non (a>5 ou c<10)


= (a ≤ b ou b ≤ c) et (a ≤ 5 et c ≥ 10)
Si l’on prend a = 4 et c = 12,
On a (4 ≤ b ou b ≤ 12), d’où on tire b = 5, p. ex.
a = 4, b = 5 et c = 12 est un jeu de test qui permet de
visiter le chemin AGI

2. chemin AGHI, condition conjuguée : FxxV (càd la condition 1 doit être fausse,
la condition 2, quelconque,
la condition 3, quelconque et
la condition 4, vraie),

expression de jeu de test : non (a>b et b>c) ET (a>5 ou c<10)


= (a ≤ b ou b ≤ c) et (a>5 ou c<10)
Si l’on prend a = 6 et c = 9,
On a (6 ≤ b ou b ≤ 9), d’où on tire b = 8, p. ex.
a = 6, b = 8 et c = 9 est un jeu de test qui permet de
visiter le chemin AGHI

3. chemin ABDFGI, condition conjuguée : VFFF (càd la condition 1 doit être vraie,
la condition 2, fausse,
la condition 3, fausse et
la condition 4, fausse),

expression de jeu de test : (a>b et b>c) ET non (a=10 ou b>20) ET


non (a<10 ou c=20) ET non (a>5 ou c<10)
= (a > b et b > c) et (a ≠ 10 et b ≤ 20) et
(a ≥ 10 et c ≠ 20) et (a ≤ 5 et c ≥ 10),
Impossible de trouver un jeu de test qui permettrait de
visiter le chemin ABDFGI à cause des conditions
a ≥ 10 et a ≤ 5,
ce chemin ne sera jamais parcouru.

4. chemin ABDFGHI, condition conjuguée : VFFV (càd la condition 1 doit être vraie,
la condition 2, fausse,
la condition 3, fausse et
la condition 4, vraie),

expression de jeu de test : (a>b et b>c) ET non (a=10 ou b>20) ET


non (a<10 ou c=20) ET (a>5 ou c<10)
= (a > b et b > c) et (a ≠ 10 et b ≤ 20) et
(a ≥ 10 et c ≠ 20) et (a > 5 et c < 10),
a = 12 et b = 8 et c = 6 est un jeu de test qui permet de
visiter le chemin ABDFGHI,

96
5. chemin ABCDEFGI, condition conjuguée : VVVF ( la condition 1 doit être vraie,
la condition 2, vraie,
la condition 3, vraie et
la condition 4, fausse),

expression de jeu de test : (a>b et b>c) ET (a=10 ou b>20) ET


(a<10 ou c=20) ET non (a>5 ou c<10)
= (a > b et b > c) et (a = 10 ou b > 20) et
(a < 10 ou c = 20) et (a ≤ 5 et c ≥ 10),
Quelle que soit la partie retenue pour que les propositions
(a = 10 ou b > 20) et
(a < 10 ou c = 20) soient vraies,
on aboutit à une contradiction,
Le chemin ABCDEFGI ne sera jamais visité.

6. chemin ABCDFGHI, condition conjuguée : VVFV ( la condition 1 doit être vraie,


la condition 2, vraie,
la condition 3, fausse et
la condition 4, vraie),

expression de jeu de test : (a>b et b>c) ET (a=10 ou b>20) ET


non (a<10 ou c=20) ET (a>5 ou c<10)
= (a > b et b > c) et (a = 10 ou b > 20) et
(a ≥ 10 et c ≠ 20) et (a > 5 ou c < 10),
a = 22 et b = 22 et c = 8 est un jeu de test qui permet de
visiter le chemin ABCDFGHI,

7. chemin ABCDEFGHI, condition conjuguée : VVVV ( la condition 1 doit être vraie,


la condition 2, vraie,
la condition 3, vraie et
la condition 4, vraie),

expression de jeu de test : (a>b et b>c) ET (a=10 ou b>20) ET


(a<10 ou c=20) ET (a>5 ou c<10)
= (a > b et b > c) et (a = 10 ou b > 20) et
(a < 10 ou c = 20) et (a > 5 ou c < 10),
a = 24 et b = 22 et c = 20 est un jeu de test qui permet de
visiter le chemin ABCDEFGHI,

97
8. chemin ABDEFGI, condition conjuguée : VFVF ( la condition 1 doit être vraie,
la condition 2, fausse,
la condition 3, vraie et
la condition 4, fausse),

expression de jeu de test : (a>b et b>c) ET non (a=10 ou b>20) ET


(a<10 ou c=20) ET non (a>5 ou c<10)
= (a > b et b > c) et (a ≠ 10 et b ≤ 20) et
(a < 10 ou c = 20) et (a ≤ 5 et c ≥10),
Si c = 20, on aboutit à la contradiction b > 20 et b ≤ 20,
Si a < 10, on aboutit à une autre contradiction sur b,
Le chemin ABDEFGI ne sera jamais parcouru,

9. chemin ABDEFGHI, condition conjuguée : VFVV ( la condition 1 doit être vraie,


la condition 2, fausse,
la condition 3, vraie et
la condition 4, vraie),

expression de jeu de test : (a>b et b>c) ET non (a=10 ou b>20) ET


(a<10 ou c=20) ET (a>5 ou c<10)
= (a > b et b > c) et (a ≠ 10 et b ≤ 20) et
(a < 10 ou c = 20) et (a > 5 ou c < 10),
a = 7 et b = 5 et c = 3 est un jeu de test qui permet de
visiter le chemin ABDEFGHI.

98
Exemple : Exécutez un test de couverture de tous les chemins à partir d’un
ordinogramme issu de l’algorithme suivant :

début
tant que a ≤ b faire a ← a + y
b←b*h
si a > c alors c ← g + x
sinon c ← g - x
d←a+b
fin

Une transcription possible sous forme d’ordinogramme (où tous les arcs sont nommés)
est :

début
a

vrai
a>b

b g
a←a+y d←a+b

c h
fin
b←b*h

vrai
a≤c
i

e
c←g+x c←g-x

f j

Les chemins élémentaires sont : a g h (chemin 1),


a b c d e f g h (chemin 2),
a b c d i j g h (chemin 3).

De manière pratique, le prédicat chemin du chemin 3, par exemple, passe par le calcul
de tous les prédicat arcs des arcs non triviaux (arcs issus de nœuds de décision)
- à savoir a ≤ b, a > b, a > c et a ≤ c –
qui composent ce chemin, à l’aide du tableau suivant :

99
a≤b a>b a>c a≤c

h - - - -

g - a>b - -

j - a>b - -

i - a>b - a≤c

d - a>b*h - a≤c

c - a+y>b*h - a+y≤c

b a≤b a+y>b*h - a+y≤c

a a≤b a+y>b*h - a+y≤c

chemin visé (à l’envers)

Chaque ligne hérite d’office des prédicats de la ligne précédente et remplace éventuel-
lement les variables par leur expression (p. ex, sur l’arc d, on sait que la variable b
a reçu la valeur b * h, donc toutes les variables b de la ligne précédente (arc i)
doivent être remplacées par b * h).
Le ‘-‘ spécifie un prédicat trivial qui n’apporte aucune information sur la traversée de
ce chemin, comme par exemple, en h, le prédicat trivial est d = a + b.

Le prédicat chemin du chemin 3 est une conjonction des prédicats de la dernière ligne
du tableau ci-avant et est :
a ≤ b ET a + y > b * h ET a + y ≤ c.

En intégrant les éléments de ce prédicat chemin de proche en proche, on peut obtenir le


jeu de test a = 3, b = 10, y = 100, h = 4, c = 800 qui permet de visiter le chemin
correspondant.

D2. Couverture de chaque instruction (C0)

A l’aide de ce critère, on voudrait que chaque instruction du programme soit exécutée


au moins une fois avec les jeux de test à trouver.
Techniquement, il s’agit de trouver un ensemble T de jeux de test qui couvrent au
moins une fois chaque instruction du programme.
Le problème principal ici est de trouver un ensemble T minimal.

100
D3. Couverture de chaque branche (C1)

A l’aide de ce critère, on voudrait que chaque sortie de tous les nœuds de décision
soient explorés par des jeux de test.
Techniquement, il s’agit de trouver un ensemble T de jeux de test qui couvrent au
moins une fois toute sortie de tous les noeuds de décision du programme.
Le problème principal ici est de trouver un ensemble T minimal.

Le critère C1 semble être un critère plus efficace que le critère C0 dans beaucoup de
cas.

Pour les nœuds qui contiennent des conditions multiples, on pourra décider si chacune
de ses conditions simples doit être évaluée indépendamment comme vraie et comme
fausse. Donc, toutes les combinaisons des conditions composant une condition
multiple seront testées en omettant des évaluations inutiles (ex : dans une condition
multiple composée des conditions P1 et P2, liées par ET, si l’évaluation de P1 est
fausse alors P2 n’a pas besoin d’être évaluée).
Les jeux de test qui seront fournis balayeront alors des combinaisons de toutes les
conditions simples composant les conditions multiples.

D4. Couverture de flot de données

Ce critère s’appuie sur la circulation de données à l’intérieur de programmes, à partir


de l’endroit où elles sont définies jusqu’à celui où elles sont utilisées.
Cette circulation est contrôlée à l’aide d’une matrice de paramètres.

La définition d’une donnée («def») correspond à l’attribution d’une valeur à une


variable.
L’utilisation d’une donnée correspond :
- soit à une utilisation pour un calcul («cUse») lorsque la variable relative à cette
donnée apparaît dans l’expression à droite d’une instruction d’affectation,
- soit à une utilisation dans un prédicat («pUse») lorsque la variable relative à cette
donnée sert à évaluer une instruction conditionnelle.

Un chemin sans définition pour un objet («Def-free») va de la définition de cet objet à


une utilisation sans passer par une redéfinition de cet objet.

Ce critère permet ainsi d’analyser les chemins qui ont certaines caractéristiques telles
que : - chaque définition de variable reliée à un «cUse» par un chemin «Def-free»,
- chaque définition de variable reliée à un «pUse» par un chemin «Def-free»,
- chemins qui utilisent des variables qui n’ont jamais été définies, ...

101
Exemple : Déterminez la matrice des paramètres relative au graphe de contrôle qui a
été tracé ci-avant :

NOEUD, ARC “Def” “cUse” “pUse”


A a, b, c, x, y - -

AB, AG - - a, b, c

B a, x a, x -

BC, BD - - a, b

C b, x b, y -

D - - -

DE, DF - - a, c

E b, y b -

F a, y a, b, x, y -

G - - -

GH, GI - - a, c

H b, x c, x

I x, y

Dans cette matrice des paramètres, par exemple, le chemin AGI est un chemin sans
définition pour la variable y.

D5. Couverture aléatoire

Cette couverture regroupe les jeux de test qui sont produits essentiellement sur base
d’une génération des nombres aléatoires ou d’une liste d’erreurs fréquentes :
- des erreurs de référence aux données (mauvaises valeurs d’indices, de pointeurs, de
qualification de variables, …),
- des erreurs de déclaration de données (mauvaises initialisations, …),
- des erreurs de calcul (compatibilité des types d’opérandes, overflow, …),
- des erreurs de comparaison,
- des erreurs algorithmiques (boucles infinies, …),
- des erreurs d’I/O (mauvaises conditions de fin de fichier, …).

102
Le test est un processus intrinsèquement incomplet et fastidieux.
Si l’on veut atteindre un degré raisonnable de confiance, plusieurs critères doivent
généralement être considérés dans la production des jeux de test.

E3. DEMONSTRATION

A. INTRODUCTION

La preuve de correction d’un programme après son écriture est, comme on l’a déjà précisé
ci-avant, très difficile à construire.
Pratiquement, une démonstration de programme revient alors à une démarche qui consiste
à construire cette preuve, à partir des assertions connues, pendant sa conception.
Cette conception, par la configuration ascendante ou descendante (cfr chap II), devrait
livrer, sur base d’un processus de raffinement successif, les instructions
qui composent le programme à construire.
Il est important de souligner que la démonstration contient en fait 2 approches :
l’approche démonstrative et l’approche constructive.
Il est beaucoup plus pratique de percevoir les aspects de base de l’approche démonstrative
avant d’aborder l’approche constructive.

B. APPROCHE DEMONSTRATIVE

Cette approche utilise massivement les formules de correction sur les constructions de base
et de simplification des assertions vus aux chapitres 2 et 3.
Il s’agira donc d’intégrer d’abord certains aspects importants sur la production des
invariants et des démonstrations simples (considérés comme des modèles) avant d’aborder
des structures plus complexes.

On doit noter qu’il est important, lors d’une démonstration, de démontrer d’abord les
structures itératives avant les autres structures, cela simplifie la démonstration et améliore
sa visibilité.

Un but précis a été assigné à chacun des différents travaux et exercices dirigés qui suivent
afin de permettre d’aborder un ensemble plus ou moins complet de modèles pouvant aider
à la démonstration des projets plus vastes.

B1. Formalisation correcte des spécifications et de leur affaiblissement

Exemple 1 :
Démontrez l’algorithme : début
i←1
fac ← 1
tant que i < n faire i←i+1
fac ← fac * i
fin

103
Réponse :

la spécification de cet algorithme est : calculer n ! (n ≥ 0),


n
sa post-condition R peut donc être : (fac = ∏ k) Λ (i = n),
k=1

l’invariant de sa boucle (qui fait pratiquement un seul corps avec cet algorithme)
peut être obtenu par affaiblissement de la post-condition (cad en transformant les
éléments nécessaires de la post-condition en leur forme de récurrence),

l’affaiblissement de R, dans ce cas-ci, fournit l’invariant I suivant :


i
(fac = ∏ k) Λ (i ≤ n).
k=1

En configuration ascendante, la démonstration I Λ B => I (cad que l’exécution du


corps de la boucle, autrement dit de la séquence i ← i + 1 et fac ← fac * i, ne
détruit pas l’invariant) doit prouver que dans la séquence :

R2
i←i+1
R1
fac ← fac * i
I

en partant de I, l’assertion R2 est vraie. La démonstration est donc :

- (i ← i + 1, -(fac ← fac * i, I)) =

i
(fac * i = ∏ k) Λ (i ≤ n) = R1
k=1

i+1
(fac * (i + 1) = ∏ k) Λ (i + 1 ≤ n) = R2
k=1

On peut voir que I est encore respecté dans R2

104
En configuration descendante, la démonstration I Λ B => I (cad que l’exécution du
corps de la boucle, autrement dit de la séquence fac ← fac * i et i ← i + 1, ne
détruit pas l’invariant) doit prouver que dans la séquence :

I
i←i+1
R1
fac ← fac * i
R2

en partant de I, l’assertion R2 est vraie. La démonstration est donc :

+ (fac ← fac * i, + (i ← i + 1, I)) =

R1 = I [i \ i0] Λ i = E[i \ i0]

i0
= (fac = ∏ k) Λ (i0 ≤ n) Λ (i = i0+ 1)
k=1

i0
= (fac = ∏ k) Λ (i0 ≤ n) Λ (i0 = i - 1)
k=1

i-1
= (fac = ∏ k) Λ (i – 1 ≤ n)
k=1

R2 = R1 [fac\fac0] Λ fac = E [fac\fac0]


i-1
= (fac0 = ∏ k) Λ (i - 1 ≤ n) Λ fac = fac0 * i
k=1

i-1
= (fac = ∏ k * i) qui est vrai (car c’est la définition d’une factorielle)
k=1

On a donc démontré que I est encore respecté dans R2

Démontrons que I Λ ‾ B => R :


i n
( (fac = ∏ k) Λ (i ≤ n) ) Λ (i ≥ n) = (fac = ∏ k) Λ (i = n) = R,
k=1 k=1

105
Démontrons que la pré-condition Q de la boucle implique I :
la pré-condition Q = (n = n0) Λ (i = i0) Λ (fac = fac0),
injectons ces éléments dans I :

i0
(fac0 = ∏ k) Λ (i0 ≤ n0)
k=1

(1) (2)

On constate aisément que (1) est vérifié car :


i0 1
(fac0 = ∏ k) = (fac0 = ∏ k = 1) 1 est bien la valeur initiale de fac
k=1 k=1

(2) qui fournit (1 ≤ n0) ne peut être vérifié que si n > 0,


Bien que cet algorithme est bon même pour n = 0, l’invariant I est un invariant faible
car Q n’est pas respecté pour n = 0.
Pour que I soit un invariant fort pour cet algorithme, il faut ajouter (n0 > 0) dans Q.

Exemple 2 :
Démontrons l’algorithme de recherche dichotomique (dans le tableau a[1:n])
suivant :

p 1
q n
h false
tant que (non) h Λ p ≤ q faire k [(p+q) /2]
si x = a[k] alors h true
sinon si x > a [k]
alors p k+1
sinon q k -1

Spécification informelle : cet algorithme détermine si l 'élément x € a [1:n].

Les éléments de spécification concrète :


PRE : n ≥ 0 Λ a [1:n] trié en ordre croissant (¥i : 1 ≤ i < n  a[i] ≤ a[i+1])
POST : h x € a[1:n] càd
(h Λ x € a[1:n]) V (non (h) Λ non (x € a [1 :n]))

106
Pour démontrer la cohérence de cet algorithme par rapport à sa PRE et sa POST :
- calculons l’assertion après les 3 initialisations juste avant la boucle :
ass3 = PRE Λ p=1 Λ q=n Λ h=false
qui constitue la pré-assertion de la boucle,
- trouvons un invariant I à la boucle en cherchant un affaiblissement de POST
qui doit caractériser un résultat partiel après un nombre quelconque
d’itérations :
I : (h (x € a[1:p-1] V x € a[q+1:n])) Λ
1 ≤ p ≤ q+1 ≤ n+1
Ou
I : (h Λ (x € a[1:p-1] V x € a[q+1:n])) V
(non (h) Λ (non (x € a[1:p-1]) Λ non (x € a[q+1:n]))) Λ
1 ≤ p ≤ q+1 ≤ n+1,

- vérifions si I est un bon invariant :


1. ? ass3  I ?
Injectons ass3 dans I :
PRE Λ p=1 Λ q=n Λ h=false  1 ≤ 1 ≤ n+1 ≤ n+1 Λ
(false (x € a[1:0] V x € a[n+1:n]))

faux faux

vrai
donc, ass3  I,

2. ? I Λ non (B)  POST ?


I Λ non (B) = I Λ (h V p>q) =
(I Λ h) V (I Λ p>q),
L’une des expressions doit être vraie.

Démontrons (I Λ h) :
(I Λ h) = (h Λ (x € a[1:p-1] V x € a[q+1:n])) Λ
(1 ≤ p ≤ q+1 ≤ n+1) Λ h.
Puisque x € a[1:p-1] ou x € a[q+1:n]  x € a[1:n],
(I Λ h) = (h Λ x € a[1:n])  POST

Démontrons (I Λ p>q) :
(I Λ p>q) = (non (h) Λ (non (x € a[1:p-1]) Λ non (x € a[q+1:n]))) Λ
(1 ≤ p ≤ q+1 ≤ n+1) Λ (p>q).
A cause de l’invariance de I, il est certain que :
p>q  p = q+1  p ≤ q+1
Donc, p-1 = q et
(I Λ p>q) = (non (h) Λ (non (x € a[1:q]) Λ non (x € a[q+1:n]))) Λ
(1 ≤ p ≤ q+1 ≤ n+1) Λ (p>q)

(non (h) Λ (non (x € a[1:n])
 POST.

107
3. ? I Λ B  I ? :
Par la configuration descendante (évaluation progressive) :
Q = + (k [(p+q)/2], I Λ B) =
(non (h) Λ (non (x € a[1:p-1]) Λ non (x € a[q+1:n]))) Λ
(1 ≤ p ≤ q+1 ≤ n+1) Λ (k = (p+q)/2)

T = + (si x = a[k] alors h true


sinon (si x > a[k] alors p k + 1 sinon q k – 1), Q) =

+ (h true, Q Λ x=a[k]) V
+ (si x > a[k] alors p k + 1 sinon q k -1, Q Λ x≠a[k] =

+ (h true, Q Λ x=a[k]) V
+ (p k + 1, Q Λ x≠a[k] Λ x>a[k]) V
+ (q k -1, Q Λ x ≠a[k] Λ x≤a[k]) =

+ (h true, Q Λ x=a[k]) V
+ (p k + 1, Q Λ x>a[k]) V
+ (q k – 1, Q Λ x<a[k]).

Examinons les 3 parties de cette assertion séparément et montrons dans


chaque cas que l’assertion résultante implique I :

partie 1 : + (h true, Q Λ x=a[k]) =


(h = true Λ (1 ≤ p ≤ q+1 ≤ n+1 Λ non (x € a[1:p-1]) Λ
non (x € a[q+1:n]) Λ x = a[k] Λ k = (p + q)/2) =

(h = true) Λ (1 ≤ p ≤ q+1 ≤ n+1) Λ non (x € a[1:p-1]) Λ


non (x € a[q+1:n] Λ x = a[(p+q)/2], (*)

par p ≤ q+1, on déduit : p - 1 < q + 1

(p - 1) + (q + 1)
d’où p -1 < <q+1
2
et

p+q
d’où p -1 < < q + 1.
2

Si l’on pose p+q/2 = p’ - 1,


alors p’ - 1 < q + 1 => p’ < q,
dès lors, il est toujours possible de modifier q tel que
p’ ≤ q’ + 1.

108
L’assertion (*) ci-dessus devient finalement
(h = true) Λ (1≤ p’ ≤ q’+1 ≤ n +1) Λ x € a[1: p’-1] Λ

(1) (2) (3)

non (x € a[q’+1:n]) Λ x = a[(p+q)/2].

Etant donné que k = p’-1 = (p+q)/2 et que x = a[k] = a[p’-1] = a[(p+q)/2],


alors x € a[1: p’-1] tel que indiqué dans (3) ci-dessus.

Donc (1), (2) et (3) impliquent I,

partie 2 : + (p k + 1, Q Λ x>a[k]) =
(non (h) Λ 1 ≤ p0 ≤ q+1 ≤ n+1 Λ non (x € a[1:p0-1]) Λ non (x € a[q+1:n])
Λ (k=(p0+q)/2) Λ ( x>a[k]) Λ (p=k+1) =

non (h) Λ 1 ≤ p0 ≤ q+1 ≤ n+1 Λ non (x € a[1:p0-1]) Λ non (x € a[q+1:n])


Λ x > a[p-1] Λ p-1 = (p0 + q)/2.

De l’expression p -1 = (p0 + q)/2, on déduit : p0 ≤ p - 1 ≤ q


(car si x = (a+b)/2, alors x est tel que a ≤ x ≤ b) et,
comme le programme ne modifie pas les a[i], on déduit de l’assertion ci-
haut :
non (h) Λ 1 ≤ p ≤ q+1 ≤ n+1 Λ non (x € a[1:p-1]) Λ
non (x € a[q+1:n])  I,

partie 3 : + (q k + 1, Q Λ x<a[k]) est évidemment symétrique et on déduit


que I est impliqué.

Pour démontrer que cet algorithme se termine, il faudrait simplement. au vu du


corps de la boucle, trouver une fonction de terminaison f pourrait être :
f(h,p,q) = (q-p+1).g(h)
où g(h) = 1 si h = false
= 0 sinon.

109
B2. Réutilisabilité des éléments des modèles

En fonction de la structure des algorithmes ou des assertions, on peut se référer à des


démonstrations existantes (par absurde, par généralisation, par spécialisation, par
induction, …) en vue d’en réutiliser des éléments dans d’autres démonstrations.

Démontrez que l’algorithme : début


i←1
fac ← 1
tant que i < n faire fac ← fac * i
i←i+1
fin

ne calcule pas n !

Etant donné la structure de cet algorithme (proche de celui du point B1 ci-dessus),


si nous supposons qu’il calcule n ! (par absurde), sa post-condition R serait :
n
(fac = ∏ k) Λ (i = n),
k=1

et donc en affaiblissant R, l’invariant I serait :


i
(fac = ∏ k) Λ (i ≤ n).
k=1

110
En configuration descendante, la démonstration I Λ B => I (cad que l’exécution du
corps de la boucle, autrement dit la séquence fac ← fac * i et i ← i + 1, ne détruit
pas l’invariant) doit prouver que dans la séquence :

I
fac ← fac * i
R1
i←i+1
R2

en partant de I, l’assertion R2 est vraie. La démonstration est donc :

+ (i ← i + 1, +(fac ← fac * i, I)) =

i
avec I = ((fac = ∏ k) Λ (i ≤ n)),
k=1

R1 = I [fac\fac0] Λ fac = E [fac\fac0] =

i
= (fac0 = ∏ k) Λ (i ≤ n) Λ fac = fac0 * i
k=1

i i
= (fac0 = ∏ k) Λ (i ≤ n) Λ (fac = ∏ k * i) = R1
k=1 k=1
i
(fac = ∏ k * i) Λ (i ≤ n) = R1
k=1

en exécutant i ← i + 1

R2 = R1 [i \ i0] Λ i = E[i \ i0] =

i0
= (fac = ∏ k * i0) Λ (i0 ≤ n) Λ (i = i0+ 1) (*)
k=1

i0
= (fac = ∏ k * i0) Λ (i0 = i - 1) (**)
k=1

Ceci est une aberration dans le calcul de la factorielle; le passage


de la valeur i0 à la nouvelle valeur i (cfr (*)) signifie que la valeur
i0 a déjà été prise en compte dans le calcul de la factorielle, donc
fac0 devrait être multiplié par i et pas par i0.

111
En configuration ascendante, la démonstration I Λ B => I (cad que l’exécution du
corps de la boucle, autrement dit la séquence fac ← fac * i et i ← i + 1, ne détruit
pas l’invariant) doit prouver que dans la séquence :

R2
fac ← fac * i
R1
i←i+1
I

en partant de I, l’assertion R2 est vraie. La démonstration est donc :

+ (fac ← fac * i, +( i ← i + 1, I)) =

R1 = I [i \ i+1] =
i+1
= (fac = ∏ k) Λ (i + 1 ≤ n)
k=1

R2 = R1 [fac \ fac * i] =
i+1
= (fac * i = ∏ k) Λ (i + 1 ≤ n)
k=1

Ceci est une aberration dans le calcul de la


factorielle car fac vaut déjà
i+1
∏k
k=1

et, il est incorrect de le multiplier encore par i.

112
B3. Utilisation de généricité et Mise en forme algorithmique

Au travers de l’exemple suivant, nous montrons qu’il peut parfois être difficile de
dériver simplement par affaiblissement un invariant à partir des assertions qui sont très
génériques.

Soit l’ordinogramme :

Début

i 1
1

j 1
1

k 1
1

F F
a[i]=b[j]=
a[i]<b[j] b[j]<c[k]
c[k]

x a [i] i i+1 j j+1 k k+1

Fin

Il est demandé :
a. d’établir sa spécification,
b. de le démontrer.

Il est généralement nécessaire et impératif de traduire l’ordinogramme donné en son


pseudo-code correspondant afin de mieux visualiser la structure de la solution
(mise en forme algorithmique).

113
Un pseudo-code possible est :

début
i 1
j 1 initialisation de la recherche
k 1
tant que a[i] ≠ b[j] ≠ c[k] faire
si a[i] < b[j]
alors i i+1 recherche du plus petit élément x commun
sinon si b[j] < c[k] aux tableaux a[], b[] et c[]
alors j j+1
sinon k k+1

x a[i] affectation de x
fin H

structure

La spécification informelle est l’affectation à x de la plus petite valeur commune w aux


tableaux a[1..m], b[1..n] et c[1..p] triés en ordre strictement croissant,
w doit exister dans a[1..m], dans b[1..n] et dans c[1..p].

Une 1ère spécification concrète par assertions proposée est :


E = (a[1..m], b[1..n], c[1..p])
D = (a[i] € N, ¥ i € [1..m], m € Ν Λ
b[j] € N, ¥ j € [1..n], n € Ν Λ
c[k] € N, ¥ k € [1..p], p € Ν)
PRE = (a[i] < a[i+1] ¥ i € [1..m-1] Λ
b[j] < b[j+1] ¥ j € [1..n-1] Λ
c[k] < c[k+1] ¥ k € [1..p-1] Λ
#{w | w € a[1..m] Λ w € b[1..n] Λ w € c[1..p]} ≥ 1 )
INV = ?
S= x
I= x€N
POST = (x = min {z | z € a[1..m] Λ z € b[1..n] Λ z € c[1..p]} )

Une 2ème spécification concrète par assertions proposée est :


E = (a[1..m], b[1..n], c[1..p])
D = (a[i] € N, ¥ i € [1..m], m € Ν Λ
b[j] € N, ¥ j € [1..n], n € Ν Λ
c[k] € N, ¥ k € [1..p], p € Ν)
PRE = (a[i] < a[i+1] ¥ i € [1..m-1] Λ
b[j] < b[j+1] ¥ j € [1..n-1] Λ
c[k] < c[k+1] ¥ k € [1..p-1] Λ
#{w | w € a[1..m] Λ w € b[1..n] Λ w € c[1..p]} ≥ 1 )
INV = ?
S=x
I=x€T
POST = (1 ≤ i ≤ m Λ 1 ≤ j ≤ n Λ 1 ≤ k ≤ p Λ
a[i] = w Λ b[j] = w Λ c[k] = w Λ x = w)

114
La post-condition de la 1ère spécification concrète qui utilise l’opérateur «min» vise à
obtenir tous les éléments communs aux 3 tableaux alors que la boucle de ce
programme s’arrête dès que le plus petit élément commun est connu.
En approche constructive, cette post-condition produira un programme très
générique (par rapport à la spécification informelle fournie) car il pourra délivrer tous
les éléments communs aux 3 tableaux au cas où ils en contiendraient plus d’un
élément commun.

Il faudra noter qu’une TROP forte généricité au niveau des assertions peut empêcher,
dans certains cas, de dériver un invariant correct simplement par affaiblissement.

Par affaiblissement :
- l’invariant I issu de la post-assertion de la 1ère spécification concrète peut être :
I = (T = {z | z € a[1..mx] Λ z € b[1..nx] Λ z € c[1..px]}) Λ
(1 ≤ mx ≤ m) Λ (1 ≤ nx ≤ n) Λ (1 ≤ px ≤ p) Λ (¥ z € T : z ≥ w),

- l’invariant I issu de la post-assertion de la 2ème spécification concrète est :


I = (1 ≤ i ≤ m Λ 1 ≤ j ≤ n Λ 1 ≤ k ≤ p Λ
a[i] ≤ w Λ b[j] ≤ w Λ c[k] ≤ w).

Démontrons ce programme sur base de l’invariant I issu de la post-assertion de la 2ème


spécification concrète avec la pré-condition Q = (i = 1 Λ j = 1 Λ k = 1) :

1. Q  I est trivial car


I = (1 ≤ 1 ≤ m Λ 1 ≤ 1 ≤ n Λ 1 ≤ 1 ≤ p Λ
a[1] ≤ w Λ b[1] ≤ w Λ c[1] ≤ w) est naturellement vrai,
2. I Λ B  I,
on doit montrer que l’exécution du corps de la boucle ne détruit pas l’invariant,
par la configuration descendante, nous avons (cfr pseudo-code ci-avant) :
+ (si a[i] < b[j] alors i i + 1 sinon H)
= + (i i + 1, I Λ (a[i] < b[j])) V (H, I Λ (a[i] ≥ b[j]))
= + (i i + 1, I Λ (a[i] < b[j])) V
+ (si b[j] < c[k] alors j j+1 sinon k k+1, I Λ (a[i] ≥ b[j])
= + (i i +1, I Λ (a [i] < b [j])) …………………….… (1)
V + (j j + 1, I Λ (a[i] ≥ b[j]) Λ (b[j] < c[k])) ….... (2)
V + (k k + 1, I Λ (a[i] ≥ b[j]) Λ (b[j] ≥ c[k]) …... (3).

Montrons que I est établi dans (1) :


(1 ≤ i0 ≤ m Λ 1 ≤ j ≤ n Λ 1 ≤ k ≤ p Λ
a[i0] ≤ w Λ b[j] ≤ w Λ c[k] ≤ w ) Λ
(a[i0] < b[j]) Λ (i = i0 + 1),
dans cette assertion, i désigne la nouvelle valeur de l’indice du tableau a et i0
sa valeur précédente, par conséquent I est vrai,
3. I Λ non(B) R :
(1≤ i ≤ m Λ 1 ≤ j ≤ n Λ 1 ≤ k ≤ p Λ
a[i] ≤ w Λ b[j] ≤ w Λ c[k] ≤ w) Λ
(a[i] = w Λ b[j] = w Λ c[k] = w) 
 (1 ≤ i ≤ m Λ 1 ≤ j ≤ n Λ 1 ≤ k ≤ p Λ a[i] = w Λ b[j] = w Λ c[k] = w) Λ
(x = a[i] = w)

115
B5. Gestion des bornes

Les cas typiques concernent des manipulations des bornes (essentiellement des
inclusions, des exclusions et des modifications de limites).

Etant donné un tableau a[1..n] d’entiers, et soit l’algorithme :

début
i 1
j n
tant que i ≤ j faire si a [i] < 0
alors i i+1 G
sinon si a [j] ≥ 0
alors j j-1
sinon a [i] a [j] H
i i+1
j j-1
fin

a. sa spécification informelle est :


déplacement des éléments du tableau a[1..n] de façon telle que ses éléments
< 0 soient à sa gauche et ses éléments ≥ 0 soient à sa droite.
Un invariant correct I de la boucle est :
I = ( ¥ k € [1..i-1] : a [k] < 0 Λ
¥ k € [j+1..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ
1 ≤ i ≤ j + 1 ≤ n + 1),
a0[1..n ] étant la copie originale de a[1..n].

Etant donné l’invariant I, la pré-condition Q de cette boucle est :


Q = (i = 1) Λ (j = n),

b. une spécification concrète par assertions peut être :


E = (a0[1..n])
D = (a0[i] € N, ¥ i : 1 ≤ i ≤ n, n € N)
PRE = Q
INV = ( ¥ k € [1..i-1] : a [k] < 0 Λ ¥ k € [j+1..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ 1 ≤ i ≤ j+1 ≤ n+1)
S = (a[1..n]
I = (a[i] € N, ¥ i : 1 ≤ i ≤ n, n € N)
POST = ((( il existe k, 1 ≤ k ≤ n | a[j] < 0 ¥ j : 1 ≤ j ≤ k Λ
a[j] ≥ 0 ¥ j : k+1 ≤ j ≤ n)
V (a [j] < 0 ¥ j : 1 ≤ j ≤ n)
V (a[j] ≥ 0 ¥ j : 1 ≤ j ≤ n) )
Λ (a[1..n] est une permutation de a0[1..n]) )

116
c. une 2ème spécification concrète par assertions peut être :
E = (a0[1..n])
D = (a0[i] € N, ¥ i : 1 ≤ i ≤ n, n € N)
PRE = Q
INV = ( ¥ k € [1..i-1] : a [k] < 0 Λ ¥ k € [j+1..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ 1 ≤ i ≤ j + 1 ≤ n + 1)
S = (a[1..n]
I = (a[i] € N, ¥ i : 1 ≤ i ≤ n, n € N)
POST = (¥ k € [1..q], a[k] < 0 Λ
¥ k € [q+1..n], a [k] ≥ 0 Λ
q € [0..n] Λ
a[1..n] est une permutation de a0[1..n])

La post-condition POST qui exprime de manière simple et concise l’état de sortie


est celle de la 2ème spécification concrète car elle contient des phrases beaucoup plus
courtes et n’utilise pas de connecteur «ou» (V), qui est moins facile à manipuler.

La condition i ≤ j+1 dans I  i-1 < j+1


 i < j+2
 i ≤ j+1

Une démonstration possible, en configuration ascendante, est :


1. pour Q  I :
cela est trivial car les intervalles [1..0] et [n+1..n] sont vides,

2. pour I Λ B  I, en sortant du bloc d’instructions H (cfr algorithme) :


-(si a[j] ≥ 0 alors j j – 1 sinon a[i] a [j]; i i + 1; j j - 1, I) =
(a [j] ≥ 0 Λ – (j j – 1, I) (**)
V
(a[j] < 0 Λ – (a[i] a [j]; i i + 1; j j - 1, I)) (*)

Montrons que I est établi dans (*) :


R1 = (j j – 1, I) = ( ¥ k € [1..i-1] : a [k] < 0 Λ
¥ k € [j..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ
1 ≤ i ≤ j ≤ n + 1),

R2 = (i i + 1, R1) = ( ¥ k € [1..i] : a [k] < 0 Λ


¥ k € [j..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ
1 ≤ i+1 ≤ j ≤ n + 1),

R3 = (a [i] a [j], R2) = ( ¥ k € [1..i] : a [k] < 0 Λ


¥ k € [j..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ
1 ≤ i+1 ≤ j ≤ n + 1),
I est établi dans (*) car a[j] est négatif et a été permuté avec a[i] qui est positif,
donc, la position i est occupée par un nombre négatif et
la position j, par un nombre positif, et
i+1 ≤ j (de R3)  i ≤ j+1 (de I) vu que

117
i+1 ≤ j  i < j
 i < j+1
(or i < j+1 est inclus dans i ≤ j+1),
a[1..n] est encore une permutation de a0[1..n] car 2 de ses
éléments ont seulement été permutés,

Montrons que I est établi dans (**) :


R4 = (j j – 1, I) = ( ¥ k € [1..i-1] : a [k] < 0 Λ
¥ k € [j..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ
1 ≤ i ≤ j ≤ n + 1),
I est établi dans (**) car a[j] qui est positif occupe déjà la portion des
nombres ≥ 0,
rien n’a été déplacé, donc a[1..n] est encore une permutation
de a0[1..n], et
i ≤ j (de R4)  i ≤ j+1 (de I) vu que
i ≤ j  i ≤ j+1),

3. pour I Λ B  I, en sortant de l’instruction G (cfr algorithme) :


-(si a[i] < 0 alors i i + 1,I) = ( ¥ k € [1..i] : a [k] < 0 Λ
¥ k € [j+1..n] : a [k] ≥ 0 Λ
a[1..n] est une permutation de a0[1..n] Λ
1 ≤ i +1 ≤ j + 1 ≤ n + 1), (***)
I est établi dans (***) car a[i] qui est négatif occupe déjà la portion des
nombres < 0,
rien n’a été déplacé, donc a[1..n] est encore une permutation
de a0[1..n], et
i+1 ≤ j+1 (de (***))  i ≤ j+1 (de I) vu que
i+1 ≤ j+1  i ≤ j+1).

Donc, généralement, nous avons :


p≤n p+i < n n–p > i
p≤n p+i ≤ n n-p ≥ i
p>n p-i ≥ n p-n ≥ i
p>n p-i > n p-n > i
p≠n p+i ≠ n+i

Dans la plupart des cas, il suffira de déterminer ou de connaître la valeur des bornes
après l’itération pour écrire correctement les assertions nécessaires.

118
C. APPROCHE CONSTRUCTIVE

Cette approche permet, à l’aide des transformateurs d’assertions, de générer les


composants principaux ou les actions primitives (instructions) d’un algorithme.
Les éléments constitutifs de ce processus se basent totalement sur l’efficacité, sur la
cohérence et sur l’adéquation des assertions définies.
On peut exprimer des assertions correctes, mais qui ne sont pas adéquates.
Par exemple, dans :

Q = (b > 0)

y←b+x

R = (b > 0)

les assertions Q et R exprimées peuvent être correctes, mais elles sont inadéquates pour la
génération d’un transformateur, qui à partir de Q et de R, fournirait l’instruction y ← b + x.
C’est pour cela qu’il est généralement intéressant de ne pas négliger l’intérêt relatif des
idées intuitives dans la construction des programmes.

L’approche constructive se base sur 3 classes de techniques :


1. techniques de raisonnement par induction sur la structure des traitements,
2. techniques de raisonnement par induction sur la structure des données,
3. techniques de raisonnement selon d’autres principes d’induction.

Les transformateurs d’assertions se basent sur un certain nombre de structures


fondamentales de raisonnement dont :
- la décomposition d’un objectif en sous-objectifs (les critères de décomposition ne sont
pas indiqués dans cette approche, c’est aux concepteurs de les préciser) et la spécification
de ces sous-objectifs,
- l’analyse par cas,
- l’induction et la généralisation,
- les transformations élémentaires entre représentations de structures de données,
- l’analyse de précédence entre sous-solutions ou entre transformations élémentaires,
- les retro-parcours.

Ces structures fondamentales débouchent finalement sur des structures algorithmiques qui
leur correspondent :
- composant algorithmique (ensemble d’instructions),
- structure sélective,
- structure itérative,
- modification d’environnement (affectation),
- composition séquentielle, …
et induisent des assertions intermédiaires et/ou généralisées.

Les assertions intermédiaires récupèrent les spécifications associées aux sous-objectifs


lors de la décomposition, chaque sous-objectif est alors construit de manière à établir
l’assertion intermédiaire y associée.

119
Les assertions généralisées sont construites à partir des assertions intermédiaires associées
aux problèmes auxiliaires qui requièrent la construction des boucles.
La dérivation d’une boucle sera guidée par la contrainte que l’assertion généralisée qui lui
est associée doit être maintenue invariante par le corps de la boucle et dans l’état final de
cette boucle.

De manière pratique, l’approche constructive par induction sur la structure des traitements
permet de :
1. spécifier formellement les résultats attendus du programme ou du module sous forme de
prédicat R,
2. déterminer un invariant I (en affaiblissant R ou par d’autres moyens),
3. itérativement :
- analyser la forme de I pour savoir s’il peut contenir d’autres invariants I’ imbriqués ou
qui se suivent,
- décrire la forme générale de ces autres invariants,
4. dériver la forme finale de l’algorithme,
5. vérifier la terminaison de l’algorithme et de toutes ses boucles.

C1. Dérivation d’une Composition Séquentielle

Par la décomposition, l’allure générale de l’algorithme P à construire sera :

{pré-condition de P}
C1
{ass1}
C2
{ass2}
C3
{ass3}
.
.
.
Cm-1
{assm-1}
Cm
{post-condition de P}

où assi forme la post-condition du composant Ci et la pré-condition du


composant Ci+1.

Dans le cas le plus trivial, le composant Ci peut être traduit directement au moyen
d’une suite d’affectation afin que l’assertion intermédiaire assi soit établie.

La dérivation d’un ordre de précédence entre 2 composants Ci et Ci+1 est guidée par :
- la dépendance entre les résultats intermédiaires produits par ces 2 composants,
- la structure de données à traiter (l’ordre d’apparition des composants au sein de la
séquence reflètera l’ordre d’occurrence ou le parcours des éléments dans les
structures de données à traiter).

120
C2. Dérivation d’une Structure Itérative

Si la forme de assi (cfr B1 ci-avant), par la présence de «pour tout … »,


«il doit exister … », … suggère que le composant Ci+1 puisse être établi au moyen
d’une boucle de la forme «tant que B alors S», alors il faut :
- trouver une assertion généralisée (invariant) I telle que assi et assi+1 sont des cas
particuliers de I,
- déduire de I la condition B,
- dériver de I et des aspects généraux du problème à résoudre par Ci+1 le corps S,
vérifier que les initialisations de S => I et
que I et non (B) => la situation finale désirée par la boucle.

Mathématiquement :
si le prédicat B du contrôle de la récurrence est toujours évaluable et ne modifie pas le
contexte lié à l’exécution des instructions S qui doivent être répétées,
si B  {I} S {I}, et
si la récurrence s’arrête
alors on peut en déduire le schéma : {I} tant que B faire S {I et non B}.

Tout raisonnement sur la structure répétitive «tant que B faire S» est nécessairement de
nature inductive.

Une forme typique de raisonnement par induction sur l’itération serait :


Si l’assertion T est vraie pour une exécution de «si B alors S» (Base de l’induction),
et
si l’assertion T reste vraie pour j-1 exécutions de «si B alors S»,
alors l’assertion T restera vraie pour j exécutions de «si B alors S» (Pas inductif).

Une façon de réaliser un tel raisonnement est que l’assertion T soit l’invariant I.

C3. Dérivation d’une Structure Sélective

En construisant une suite Si+1 d’instructions pour réaliser le composant Ci+1, il faut
s’assurer que assi+1 => + (Si+1, assi ) (1)
ou assi => - (Si+1, assi+1 ) (2).

Supposons que pour (2), on trouve assi ≠> - (Si+1, assi+1 ),


mais assi Λ B => - (Si+1, assi+1 ),
cela signifie que c’est bien l’instruction : Si+1’ = si B alors Si+1 qui la relation
assi => - (Si+1’, assi+1 )

Logiquement, si on a : ({B} et {Q}) S1 {R}


Λ
({┐B} et {Q}) S2 {R},
on peut déduire le schéma : {Q} si B alors S1 sinon S2 {R}.

121
C4. Démonstration de la terminaison de programme

L’approche démonstrative ou constructive du programme P doit être complétée par la


démonstration que P et toutes ses boucles d’itération se terminent
.
Pour chaque boucle «tant que B faire S», il suffit de démontrer que l’itération passe
d’un état satisfaisant B à un état satisfaisant non (B) après avoir exécuté l’instruction
S un nombre fini de fois.

Si X dénote l’ensemble des variables et des paramètres manipulés dans S, il s’agira de


trouver une fonction f (qui permettra d’asseoir un raisonnement inductif) telle que :
1. f(x) > 0 et f(x) € Ν (x € X),
2. si B est vrai alors f(x) > 0,
3. toute exécution de S fait décroitre la valeur prise par f telle que :
{f(x)=f0} S {f(x)=f1} avec f0 > f1,
une telle fonction f atteindra nécessairement la valeur f(x)=0 en un nombre fini
d’étapes, d’où non (B) sera nécessairement établi.

122
C5. Exercices pratiques

Nous examinons ci-après quelques exercices sur l’approche constructive par induction
sur la structure des traitements qui pourraient , en principe, soutenir ou servir des
modèles à n’importe quelle conception guidée par la récurrence et l’invariant.
Ces exercices devraient permettre d’élaborer des démarches et des directives dans le
développement de projets plus vastes :

A. Soit une collection de taille modérée de m couples (ti, ki), où pour ¥ i, ti est un
entier qui identifie de manière univoque un objet i de couleur ki,
ki pouvant être bleu, noir ou rouge.
Ces couples apparaissent dans un ordre quelconque.
Il est demandé de concevoir par un raisonnement guidé par induction
sur la structure des traitements un programme qui doit réorganiser cette collection
de façon telle que tous les couples de couleur rouge soient suivis de tous les
couples de couleur noire et que ceux-ci soient suivis de tous les couples de couleur
bleue.
Il est important de souligner que toutes ces 3 couleurs ne sont pas nécessairement
présentes dans la collection.

Etapes de conception :
Graphiquement, en implémentant les entiers par le tableau t[1..m] et les couleurs
par le tableau c[1..m], par exemple, la situation finale voulue (quand
les 3 couleurs sont présentes) peut être représentée par :

ROUGES NOIRS BLEUS

0 ................ n n+1 .............. r-1 r .................... m

avec comme post-condition R =


0 ≤ n < r ≤ m+1 Λ
¥ i : (1 ≤ i ≤ n  c[i] = rouge Λ n < i < r  c[i] = noir Λ
r ≤ i ≤ m  c[i] = bleu)

On peut déjà, à partir de cette situation dire :


- que si n = 0 alors aucun couple de couleur rouge n’est présent dans la collection,
- que si n = r-1 alors aucun couple de couleur noire n’est présent dans la collection,
- que si r = m + 1 alors aucun couple bleue n’est présent dans la collection,
- que si n = m +1 alors seuls les couples rouges sont présents dans la collection, ....

Dans R, ¥ i suggère la présence d’une itération, il faut alors trouver une situation
générale qui permet de mettre en évidence ce qu’il reste à faire pour atteindre la
situation désirée par R.
Cela est généralement fait, comme on l’a vu, en affaiblissant l’assertion R.

123
Plusieurs situations générales sont possibles, nous traçons quelques unes :

1. vide (1) ROUGES vide (2) NOIRS vide (3) BLEUS


0 ………………….. n n+1 …………. r-1 r …………………... m

i1 i2 i3

dans laquelle il est créé 3 sous-ensembles disjoints d’objets à classer, ce qui


génère en fait 3 instances de raisonnement par récurrence (3 invariants),
la plage «vide (1)» pouvant recevoir les couples rouges,
la plage «vide (2)» pouvant recevoir les couples noirs,
la plage «vide (3)» pouvant recevoir les couples bleus,

2. vide ROUGES NOIRS BLEUS

dans laquelle il n’existe qu’un seul ensemble d’objets à classer (représenté


par la variable i) à gauche et qui, lorsque la collection fournit un objet bleu
nécessite une réorganisation d’objets qui ont été préalablement classés
(déplacement des couples rouges et noirs) vers la gauche,

3. ROUGES NOIRS BLEUS vide

dans laquelle il n’existe qu’un seul ensemble d’objets à classer (représenté


par la variable i) à droite et qui, lorsque la collection fournit un objet rouge
nécessite une réorganisation d’objets qui ont été préalablement classés
(déplacement des couples noirs et bleus) vers la droite,

4. ROUGES vide NOIRS BLEUS

0 …………. n n+1 ……………... d d+1 ……. r–1 r ………… m

dans laquelle il n’existe qu’un seul ensemble d’objets à classer (entre les
objets rouges et noirs déjà classés) et qui ne nécessite aucune réorganisation
lorsque la collection fournit un couple rouge ou noir).

Parmi ces 4 situations générales, retenons d’examiner de plus près la 4ème qui fournit
une solution efficace (en temps et en espace) dotée d’une très grande généricité.

124
Un invariant I correspondant à cette 4 ème situation est :
I = 0≤n ≤d<r ≤m+1 Λ
¥ i : (1 ≤ i ≤ n  c[i] = rouge Λ
d < i < r  c[i] = noir Λ
r ≤ i ≤ m  c[i] = bleu).

Cet invariant correspond bien à un affaiblissement du R (n ≤ d) et interdit que


n = d = r afin d’éviter l’incohérence d’un objet qui aurait plus d’une couleur.

Une pré-condition correcte pour la boucle ci-dessous peut être :


ass2 = (n = 0 Λ d = m Λ r = m+1),
n = 0 signifie qu’il n’y a pas de couples rouges,
d = m signifie qu’il n’y a pas de couples noirs,
r = m+1 signifie qu’il n’y a pas de couples bleus.

? ass2  I ? => I = 0 ≤ n ≤ d < r ≤ m + 1 Λ


¥ i : (1 ≤ i ≤ 0  c[i] n’existe pas Λ
m < i < m+1  c[i] n’existe pas Λ
m+1 ≤ i ≤ m  c[i] n’existe pas),

ass2 est correcte car elle exprime bien la situation avant d’entrer dans la boucle

Trouvons une bonne condition B de contrôle de la boucle et démontrons que


I Λ non(B)  R :
En examinant l’invariant de la situation générale retenue, une bonne condition de
répétition de la boucle serait n < d.

? I Λ non(B)  R ? =>
(0 ≤ n ≤ d < r ≤ m + 1 Λ
¥ i : (1 ≤ i ≤ n  c[i] = rouge Λ
d < i < r  c[i] = noir Λ
r ≤ i ≤ m  c[i] = bleu)) Λ (n ≥ d)
=>
(0 ≤ n < r ≤ m + 1 Λ
¥ i : (1 ≤ i ≤ n  c[i] = rouge Λ
n < i < r  c[i] = noir Λ
r ≤ i ≤ m  c[i] = bleu)) =R

Nous avons dès lors tous les éléments qui peuvent permettre de trouver un corps de
boucle qui puisse garantir qu’en une itération, on se rapproche de R tout en restant
dans la situation générale (invariant).
Il est évident que le parcours général de la structure de base (ici, tableau [1..m] ) se
fait de droite à gauche en fonction de la position d.

125
Tous les cas possibles en cette position d sont :
- si la couleur à mettre en position d est noire  d = d – 1 garantit la décroissance,
- si la couleur à mettre en position d est rouge  n n+1
permuter les couples des
positions d et n,
- si la couleur à mettre en position d est bleue  r r -1
permuter les couples des
positions d et r
d d–1

Cela fournit naturellement l’algorithme suivant :

n 0
r m+1
d m
tant que n < d faire
si c[d] = ‘noir’ alors d d–1 (1)
si c[d] = ‘rouge’ alors n n+1
(a, b) (t [d], c[d])
(t [d], c[d]) (t [n], c[n]) (2)
(t [n], c[n]) (a,b)
si c[d] = ‘bleu’ alors r r–1
(a, b) (t [d], c[d])
(t [d], c[d]) (t [n], c[n]) (3)
(t [n], c[n]) (a,b)
d d-1

Démontrons (2) par configuration ascendante :


R1 = -( (t[n],c[n]) (a,b), I) =
0≤n ≤d<r ≤m+1 Λ
¥ i : (1 ≤ i ≤ n  b = ‘rouge’ Λ
d < i < r  c[i] = ‘noir‘ Λ
r ≤ i ≤ m  c[i] = ‘bleu’),
donc, c[d]=c[i]=’rouge’=b
¥ i : 1 ≤ i ≤ n , (*)
R2 = -( (t [d], c[d] (t [n], c[n]), R1) =
0≤n ≤d<r ≤m+1 Λ
¥ i : (1 ≤ i ≤ n  b = ‘rouge’ Λ
d < i < r  c[i] = ‘noir’ Λ
r ≤ i ≤ m  c[i] = ‘bleu’),

R3 = -( (a,b) (t [d], c[d], R2) =


0≤n ≤d<r ≤m+1 Λ
¥ i : (1 ≤ i ≤ n  c[d] = ‘rouge’ Λ
d < i < r  c[i] = ‘noir’ Λ
r ≤ i ≤ m  c[i] = ‘bleu’),

126
R4 = -(n n + 1, R3) =
0 ≤ n+1 ≤ d < r ≤ m + 1 Λ
¥ i : (1 ≤ i ≤ n+1  c[d] = ‘rouge’ Λ
d < i < r  c[i] = ‘noir’ Λ
r ≤ i ≤ m  c[i] = ‘bleu’)
=>
0≤n ≤d<r ≤m+1 Λ
¥ i : (1 ≤ i ≤ n  c[i] = rouge Λ
d < i < r  c[i] = noir Λ
r ≤ i ≤ m  c[i] = bleu),

la condition de la répétition de la boucle étant


n < d, donc n+1 ≤ d, et
1 ≤ i ≤ n+1  1 ≤ i ≤ n.

On peut remarquer, dans (*) sur la page précédente que l’on a même dérivé la
condition (c[d] = rouge) qui doit protéger cette alternative au sein de l’itération
et qu’une bonne documentation est automatiquement fournie avec cette
démonstration.

B. Soient 2 tableaux a[1: n], b[1: n], n ≥1 où a[1] ≤ a[2] ≤ …… ≤ a[n],


il est demandé de construire dans b[1: n] une copie de a[1:n] tels que
{a[1], … , a[n]} = {b[1], …, b[m]} et b[1] < b[2] < ……… < b[m] (m ≤ n).

Les étapes essentielles d’une solution possibles sont :

1. Spécification formelle du résultat attendu


R = {a[1], … , a[n]} = {b[1], …, b[m]} Λ (b[1] < b[2] < ……… < b[m]) Λ
(m ≤ n)

2. Détermination d’un bon invariant


Une idée intuitive qui peut permettre de dégager une situation (invariant ) qui
sera toujours vérifiée peut être graphiquement représentée comme suit :

a[]:
1 k n

b[]:
1 m n
Ce qui, mathématiquement, peut être exprimée par :
I = ({a[1], …… , a[k]} = {b[1], ….., b[m]} Λ (b[1] < b[2] < ……. < b[m]) Λ
(1 ≤ m ≤ k ≤ n) ).
Logiquement, I ne peut pas contenir d’autres invariants.

3. Dérivation de la forme finale de l’algorithme


a. Construction des initialisations :
si, p.ex., k 1, m 1, b[1] a[1] alors I sera vérifié

127
b. Etablissement d’une condition C de fin d’itération telle que si I est vrai alors
R sera établi :
p. ex., C = (k=n)

c. Maintien de I à chaque boucle et terminaison du programme :


soient k1 et m1, les valeurs de k et m, si k1< n et s’il existe un passage suivant
dans le corps de la boucle, 2 cas peuvent arriver :
- a[k1+1] = b[m1], alors k k1 + 1
- a[k1+1] ≠ b[m1], alors k k1 + 1 et m m1 + 1.
Ce qui, algorithmiquement peut s’écrire :
k k+1
si a[k] ≠ b[m] alors m m+1
b[m] a[k]

On a enfin le programme suivant :

début
k 1
m 1
b[1] a[1]
tant que k ≠ n faire
k k+1
si a[k] ≠ b[m] alors m m+1
b[m] a[k]

fin

128
C. Soit une ligne constituée par une suite de n mots, séparés entre eux au moyen
d’un et d’un seul blanc (espace vide) et suivie éventuellement d’une suite de s
blancs.
On demande d’écrire un programme qui doit justifier cette ligne à gauche et à
droite, càd qui doit insérer des blancs supplémentaires entre les mots de telle
sorte que :
- le 1er caractère du 1er mot reste en 1ère position de la ligne,
- le dernier caractère du dernier mot figure soit en dernière position de la ligne,
- le désagrément causé par les espaces intercalaires supplémentaires soit
atténué de façon telle que :
(1). les nombres des blancs adjacents intercalaires ne différent entre eux que
d’au plus 1 unité,
(2). la répartition des blancs supplémentaires soit balancée de manière telle
que pour une ligne de numéro pair, il y ait davantage de blancs dans la
partie gauche de la ligne,
tandis que pour 1 ligne de numéro impair, il y ait davantage de blancs
dans la partie droite de la ligne

Ex : si, à l’entrée, on a la suite de lignes (où chaque _ désigne un espace) :


Un_éditeur_de_texte_doit_ pouvoir
justifier_les_lignes_à_gauche_ _ _
par_insertion_d’_espaces_ _ _ _ _ _ _ _

alors, en sortie, on doit avoir :


Un_éditeur_de_texte_doit_ pouvoir
justifier_ _les_ _lignes_ _à_gauche
par_ _ _insertion_ _ _ _d’_ _ _ _espaces

Implémentation :
Une structure correcte de représentation des lignes à traiter est le tableau
b[1: m].
Chaque ligne sera ainsi caractérisée par les attributs suivants :
- le numéro z de ligne,
- le nombre s de blancs à y insérer,
- le nombre n de mots dans la ligne,
- la position b[i] (1≤ i ≤ n) du 1er caractère de chaque mot.

Logiquement et généralement, une ligne peut être représentée par la relation :


M1 (k1) M2 (k2) ………………………… Mn-1 (kn-1) Mn (s) où
M1, M2, ……………., Mn sont les différents mots de la ligne,
k1, k2, …………, kn-1) sont les différents nombres d’espaces intercalaires,
s, nombre d’espaces en fin de ligne.

129
Pré-condition :
La situation initiale représentée par :
M1 (1) M2 (1) ………………………… Mn-1 (1) Mn (s)
peut être caractérisée par la pré-condition suivante :
PRE : n > 0 Λ s ≥ 0 Λ ¥i : 1 ≤ i ≤ n  b[i] = Bi Λ
¥i : 1 ≤ i < n  ki = 1.
Il faudrait en fait que n > 1 (càd qu’il y ait au moins 2 mots dans une ligne
pour pouvoir justifier).
Suite à une dérivation systématique, on pourra résoudre ce petit problème de
spécification au niveau de la programmation.

Post-condition :
La situation finale (c-à-d la ligne justifiée) représentée par :
M1 (p+1) M2 (p+1) …… Mh (q+1) Mh+1 (q+1) …… Mn-1 (q+1) Mn
peut être caractérisée par les spécifications suivantes :

POST1 : p ≥ 0 Λ q ≥ 0 Λ 1 ≤ h ≤ n Λ
p.(h – 1) + q.(n - h) = s Λ
(pair (z) Λ p = q + 1 V impair (z) Λ q = p + 1)
pour insérer davantage des blancs dans la partie gauche ou droite de
la ligne,
POST2 : ¥i : (1 ≤ i ≤ h  b[i] = Bi + p.(i-1) Λ
h < i ≤ n  b[i] = Bi + p.(h-1) + q.(i – h))
qui spécifie la place des mots Mi dans la ligne justifiée.

La post-condition POST est alors = POST1 Λ POST2 .

Analyse :
En analysant les éléments précédents, nous pouvons dire que :
1. fonctionnellement, le sous-problème non trivial (étant donné la post-
condition) est donc :
«définition de la position des mots de la ligne donnée dans la ligne justifiée»,
2. une fois p, q et h (tel que b[1:h] et b[h+1:n]) connus, la forme de POST2
suggère un parcours de la ligne à traiter,
3. pour des raisons de dépendance entre résultats intermédiaires (visibles dans
les vérités précédentes), nous pouvons aboutir à la décomposition suivante :

composant pour calculer p, q et h (C1)


composant pour calculer les nouveaux b[1:h] (C2)
composant pour calculer les nouveaux b[h+1:n] (C3)

dont l’exécution devra vérifier :


ass0 : PRE
C1
ass1 : PRE Λ POST1
C2
ass2 : POST1 Λ ¥i : (1 ≤ i ≤ h  b[i] = Bi + p.(i-1) Λ
h < i ≤ n  b[i] = Bi
C3
ass3 : POST

130
h < i ≤ n  b[i] = Bi dans ass2 exprime bien la condition que les mots au-
delà de la position h ne sont pas concernés par le calcul des nouveaux b[1:h].

3a. Calcul de p, q et h :

La forme de ass1 établi par C1 (surtout en ce qui concerne POST1 qui


contient un «ou» logique) suggère que C1 doit se baser sur une analyse
par cas selon la parité ou l’imparité du numéro z de la ligne à justifier :

si z est pair, POST1 devient :


p≥0 Λ q≥0 Λ 1≤h≤n Λ
p=q+1 Λ
p.(h – 1) + q.(n - h) = s
=>
q+1≥0 Λ q≥0 Λ 1≤h≤n Λ
p=q+1 Λ
(q + 1).(h – 1) + q.(n - h) = s
=>
q+1≥0 Λ q≥0 Λ 1≤h≤n Λ
p=q+1 Λ
q.(n – 1) + h -1 = s (1)

Il faudra donc établir que :


q≥0 Λ 1≤h≤n Λ p=q+1 Λ q.(n – 1) + h - 1 = s.

En choisissant une stratégie naturelle de répartition moyenne q qui


est égal à :

nombre de blancs à insérer


nombre total de blancs intercalaires à l’entrée

nous aurons :
q = [s / n-1] Λ
h = s + 1 – q.(n – 1) Λ tirée de (1)
p = q + 1,
et, ces 3 assertions produisent les instructions :
q [s / n-1]
h s + 1 – q.(n – 1) (*)
p q+1
et établissent POST1 dans le cas où z est pair.
Une des conditions pour que ces 3 instructions établissent PRE est que
[s / n-1] ≥ 0 (puisque q ≥ 0), c-à-d n > 1
 au moins 2 mots dans une ligne.

Si z est impair, la dérivation de la séquence d’instruction pour C1 est


analogue au cas ci-avant.

131
3b. Calcul des nouveaux b[1:h] :

La forme de l’assertion ass2 produite par C2 suggère un parcours, d’où la


nécessité d’une boucle pour C2.
La situation générale, mettant en évidence ce qu’il reste à faire pour
établir ass2, étant entendu qu’on a déjà itéré de façon correcte un nombre
quelconque de fois, est :
I2 : 1 ≤ k ≤ h Λ ¥i : (1 ≤ i ≤ k  b[i] = Bi + p.(i – 1) Λ
k < i ≤ n  b[i] = Bi) Λ POST1.

Montrons que I2 est un bon invariant :

avant la boucle :
il faut que ass1 (= PRE Λ POST1)  I2 :
POST1  I2 est trivial à cause de (*) page précédente,
PRE  I2 car chaque mot occupe encore son emplacement
originel et seul le 1er mot spécifie déjà sa place dans la
ligne justifiée,
donc, si k 1), I2 sera :
1 ≤ 1 ≤ h Λ ¥i : (1 ≤ i ≤ 1  b[i] = Bi + p.(i – 1) Λ
1 < i ≤ n  b[i] = Bi) Λ POST1
=>
b[i] = B1 + p.(1-1) = B1,

après la boucle :
il faut que I2 Λ non (B)  ass2 :
on peut aisément vérifier que la condition k = h assure
que I2  ass2.
Donc non (B) = (k=h) et la condition B de la boucle est
B = (k < h),

dans le corps de la boucle :


il faut que I2 Λ B soit vérifié,
il faut donc que I2 reste invariant et que la distance h – k décroisse
à chaque itération,
d’où, on peut dégager la séquence des instructions suivantes pour
la boucle :
k k+1
b[k] Bk + p.(k-1)
(Démontrez que l’ordre inverse de ces instructions n’assure pas
l’invariance de I2).

132
3c. Calcul des nouveaux b[h+1:n] :

En appliquant le même type de raisonnement que pour le composant C2,


on obtient l’invariant I3 suivant :
I3 : h ≤ k ≤ n Λ ¥i : (1 ≤ i ≤ h  b[i] = Bi + p.(i-1) Λ
h < i ≤ k  b[i] = Bi + p.(h-1) + q.(i – h)) Λ
k < i ≤ n  b[i] = Bi) Λ POST1.
Il faudra démontrer que I3 est un bon invariant pour cette boucle :
avant la boucle : que ass2  I3,
après la boucle : que I3 Λ non (B)  POST,
dans le corps de la boucle : que I3 Λ B  I3.

De manière similaire que ci-dessus (pour C2), on dérive la séquence des


instructions suivantes :
k k+1
b[k] b[k] + p.(h-1) + q.(k-h)

Nous obtenons finalement, le programme non trivial (en pseudo-code) et correct


suivant :

si pair (z) alorsq [s / (n-1)]


h s + 1 – q.(n -1)
p q+1
sinon p [s / (n-1)]
h p.(n -1) + n - s
q p+1

k 1
tant que k < h faire k k+1 (**)
b[k] b[k] + p.(k – 1)

k h
tant que k < n faire k k+1
b[k] b[k] + p.(h – 1) + q.(k - h)

Le programme tel qu’il est écrit est correct, cependant des optimisations
(améliorations) peuvent y être apportées, par exemple :

1. éviter de recalculer p.(k -1) à chaque itération, pour chaque k,


on peut alors capitaliser le nombre de blancs déjà insérés, d’où :
- introduction d’une nouvelle variable v dans I2, ce qui fournit :
I’2 = (I2 Λ v = p.(k–1))
- insertion d’instructions supplémentaires dérivées de I’2 :
k k+1
v v+p
b[k] b[k] + v
avec comme initialisation :
k 1 et v 0,

133
2. éviter d’exécuter inutilement (sans aucun effet) h-1 fois la boucle, lors de
l’insertion de blancs dans la partie gauche de la ligne lorsque p=0.
D’après I’2 :
si p ≠ 0 et k < h, cela entraîne que v < p.(h-1),
si p = 0, on aura tout de suite I’2 Λ non (B) sans itération, car
I’2 Λ non (B) = (v ≤ p.(h – 1)  ass2,
d’où la version optimisée pour la boucle (**) :

k 1
v 0
tant que v < p.(h -1) faire k k+1
v v+p
b[k] b[k] + v

134
D. REMARQUES IMPORTANTES

1. L’approche démonstrative et l’approche constructive reposent sur des axiomes et


des règles d’inférence qui déduisent un texte formalisant un algorithme et non sur les
multiples exécutions possible de cet algorithme.

2. Les techniques de démonstration permettent de vérifier qu’un programme est correct, eu


égard à ses spécifications concrètes, ces dernières étant supposées correctes, complètes,
adéquates, cohérentes et adéquatement formulées.
Elles ne démontrent rien en ce qui concerne la validité de ces spécifications.
Une assertion incorrecte ou incomplète génère un programme mauvais, incorrect ou
incomplet.
Une construction de programme ou toute autre démonstration peut ne pas aboutir à un
programme correct ou à une conclusion correcte à cause des erreurs dans la construction
ou la démonstration elle-même.

3. Si les assertions ou les inférences comportent des erreurs, on aboutira à une mauvaise
démonstration ou une mauvaise construction de programme.

4. Une mauvaise communicabilité de l’algorithme donne lieu généralement à une


démonstration complexe et fastidieuse.

5. La difficulté principale réside souvent dans la détermination de bons invariants I,


de bonnes fonctions de terminaison, de bonnes simplifications d’assertions et un niveau
adéquat d’expression de spécifications,
en pratique, il faudra analyser la forme de la post-condition de la boucle afin d’en
extraire une forme affaiblie I (cad qui caractérise une situation ou un résultat partiel de
la récurrence) telle que I Λ non B  I,

6. Les techniques de démonstration nécessite une analyse approfondie du problème en vue


de trouver des spécifications précises et rigoureuses.
Une erreur de compréhension dans cette analyse est généralement catastrophique pour la
suite du développement.
Les programmes assis sur des spécifications précises et rigoureuses sont toujours
conceptuellement corrects, ils peuvent toutefois être sujets à des optimisations pour des
raisons de performances.

135
F. EXEMPLES, EXERCICES ET TP

1. Considérez le premier niveau de n’importe quelle architecture où il existe un module de


coordination (coordonateur) et nommez toutes les relations qui existe entre ce module de
coordination et les composants du niveau suivant.

2. Soient les composants suivants :


- EDITION : qui doit insérer les points de séparation des milliers, millions, milliards, …
dans un nombre entier,
- TOTALISATION : qui doit totaliser les différents salaires dus à un formateur et
mémoriser ce total,
- FACTURATION : imprimer des montants édités (où des points de séparation ont été
insérés) ou non sur des notes de crédit à envoyer aux formateurs.

2a. Indiquez l’architecture qui représente correctement cette situation parmi les suivantes :

appelle appelle
a. FACTURATION EDITION TOTALISATION

b. FACTURATION

utilise

utilise
TOTALISATION EDITION

utilise
c. FACTURATION EDITION

appelle

TOTALISATION

appelle
d. FACTURATION EDITION

utilise

TOTALISATION

136
2b. Spécifiez par assertions les modules suivants :

Module 1 :
EDITION
FACTURATION

Module 2 :
TOTALISATION
FACTURATION

3. Que se passera-t-il si le fichier ARTICLE (le fichier à l’entrée) n’est pas trié sur le NOM ?
R : le programme ne fonctionnera pas car il ne pourra pas respecter sa spécification.

4. Proposez une architecture logicielle correcte de l’atelier 1 page 54 dans le cas où le fichier
ARTICLE (le fichier à l’entrée) n’est pas trié.

5. Il est demandé, à l’aide d’un raisonnement par récurrence, d’éditer un nombre n € N en y


insérant les points de séparation des milliers, millions, milliards, … .

6. Il est demandé, à l’aide d’un raisonnement par récurrence, de concevoir un algorithme qui
permet de chercher les plateaux du tableau a[1:n] (n≥1) et d’afficher leur longueur.

Un plateau du tableau a[1:n] est une portion d’éléments contigus a[i:j], 1 ≤ i ≤ j ≤ n, telle
que :
- a[i] = a[i+1] = a[i+2] = …… = a[j] et
- a[i-1] ≠ a[i] si a[i-1] existe et
- a[j] ≠ a[j+1] si a[j+1 existe.

Ex : les 6 plateaux du tableau a[1:11] = (3,3,3,3,4,4,5,9,9,5,1) et leur longueur sont :


[1,4] – 4
[5,6] – 2
[7,7] – 1
[8,9] – 2
[10,10] – 1
[11,11] – 1

7. Il est demandé, à l’aide d’un raisonnement par récurrence, de calculer le déterminant |A| d’une
matrice carrée A de dimension n x n (n ≥ 1).

137
8. Il est proposé ci-après 2 ensembles de composants logiciels qui, étant donné une ligne,
fournissent une liste ordonnée (selon l’alphabet français) de tous les shifts circulaires de cette
ligne.
Nous savons que :
- une ligne est un ensemble ordonné de mots,
- un mot est un ensemble ordonné de caractères (dont le dernier est un séparateur unique pour
tout mot),
- par exemple, les 4 shifts circulaires (ou simplement shifts) de la ligne
«Je chante sous la pluie» sont :
1. chante sous la pluie je
2. sous la pluie je chante
3. la pluie je chante sous
4. pluie je chante sous la

81. Premier ensemble de composants :

Le premier ensemble contient les 5 composants suivants :


1. ENTREE avec 3 sous-composants :
- sous-composant de lecture des lignes,
- sous-composant de reconnaissance des mots d’une ligne,
- sous-composant de mémorisation (dans la table T1) des adresses des lignes stockées,
2. SHIFT qui organise une 2ème table (T2) dans laquelle chaque 1er mot d’un shift
est identifié concrètement par le couple (adresse de la ligne,1er mot du shift)
3. CLASSEMENT qui, sur base des tables T1 et T2, produit une 3ème table (T3) qui
spécifie l’ordre alphabétique des différents shifts circulaires,
4. SORTIE qui, sur base des tables T1 et T3, formate les shits demandés pour
l’impression,
5. COORDONNATEUR qui doit gérer des erreurs et contrôler la séquence.

82. Deuxième ensemble de composants :

Le deuxième ensemble contient les 6 composants suivants :


1. MEMORISATION avec 4 sous-composants :
- sous-composant qui représente le ième caractère du jème mot de la kème ligne par une
valeur v,
- sous-composant qui renvoie le nombre de mots d’une ligne,
- sous-composant qui renvoie le nombre de caractères d’un mot d’une ligne,
- sous-composant qui supprime un mot d’une ligne,
2. ENTREE qui lit des lignes,
3. DECALAGE avec 4 sous-composants :
- sous-composant qui représente le ième caractère du jème mot du kème shift par une valeur
v,
- sous-composant qui renvoie le nombre de mots d’un shift,
- sous-composant qui renvoie le nombre de caractères d’un mot d’un shift,
- sous-composant qui supprime un mot d’un shift,
4. TRIAGE qui renvoie une valeur q représentant le mème shift dans le classement
alphabétique des shifts d’une ligne donnée,
5. SORTIE qui, imprime la liste demandée dans le format désiré,
6. COORDONNATEUR qui doit gérer des erreurs et contrôler la séquence.
Il est demandé de reproduire les 2 architectures logicielles y relatives et de les analyser.

138
9. Soient 2 tableaux a[1:n] et b[1:m] d’entiers, il est demandé d’écrire toutes les étapes d’un
raisonnement par récurrence qui vérifie si le tableau a est un SEGMENT du tableau b.

Le tableau [ai,ai+1,ai+2, …,ai+k] est un SEGMENT du tableau b s’il existe une suite contigüe
d’éléments bj,bj+1,bj+2, ….,bj+k telle que :
ai = bj et
ai+1 = bj+1 et
ai+2 = bj+2 et
…….. et
ai+k = bj+k

(exemples : [2,8,5] est un segment de [9,4,2,8,5,7,0,6,3]


[4,2,8] est un segment de [9,4,2,8,5,7,0,6,3]
[2,8,7] n’est pas un segment de [9,4,2,8,5,7,0,6,3]
[7,0,3] n’est pas un segment de [9,4,2,8,5,7,0,6,3]
[7,0,6,3] est un segment de [9,4,2,8,5,7,0,6,3]
[9,4,2,8,5,7,0,6,3] est un segment de [9,4,2,8,5,7,0,6,3]
[9,4,2,8,5,7,0,6] est un segment de [9,4,2,8,5,7,0,6,3]
[ ] est un segment de n’importe quel tableau )

.
10. Soient 2 tableaux a[1:n] et b[1:m] d’entiers, il est demandé d’écrire toutes les étapes d’un
raisonnement par récurrence qui vérifie si le tableau a est un SOUS-SUITE du tableau b.

Le tableau a est une SOUS-SUITE du tableau b si tous les éléments de a apparaissent dans b
suivant la même séquence, mais pas nécessairement de manière contigüe.

(exemples : [2,8,5] est une sous-suite de [9,4,2,8,5,7,0,6,3]


[4,2,0] est une sous-suite de [9,4,2,8,5,7,0,6,3]
[2,4,0] n’est pas une sous-suite de [9,4,2,8,5,7,0,6,3]
[4] est une sous-suite de [9,4,2,8,5]
[1] n’est pas une sous-suite de [9,4,2]
[1,2,8,7] n’est pas une sous-suite de [9,4,2,8,5,7,0,6,3]
[7,0,3] est une sous-suite de [7,0,3]
[ ] est une sous-suite de n’importe quel tableau )

11. Construisez un jeu de test «boite noire» en vue de valider toutes ou partie des spécifications
d’un programme qui doit convertir une date j/m/a (j = 2 chiffres du jour,
m =2 chiffres du mois,
a = 4 chiffres de l’année)
en un numéro julien (ex : pour la date 01/02/2010, le numéro julien est 32).

139
12. Une société concessionnaire d’automobiles, appelée CHEVAL VAPEUR envisage
d’automatiser une partie de ses activités administratives et de gestion.
Les départements concernés par ce projet sont :
- le magasin des pièces,
- l’atelier d’entretien et de réparation de véhicules,
- les services administratifs.

La société comprend actuellement 1000 clients et dispose de 10 emplacements dans son


atelier pour l’examen et le démontage des véhicules.

A la réception, toutes les informations concernant le véhicule et le client sont enregistrées par
un employé affecté à l’accueil des clients. Cet employé peut procéder à la mise à jour de toute
donnée (du client ou du véhicule) qui a changé.
Après cela, pour une demande de réparation, un ordre de réparation (OR) identifié par le
numéro de chassis du véhicule est ouvert et cet employé procède à l’identification des
éléments généraux de l’OR qui sont :
- nature (réparation, devis, entretien sous garantie, entretien hors garantie),
- liste de travaux (y compris les travaux particuliers demandés par le client),
- délai de finition.
Un OR reste ouvert jusqu’à la fin des travaux.
Il faut savoir que la société n’exécute que des travaux standards, que chaque travail n’est pas
interruptible et qu’un entretien sous garantie ne donne lieu à aucune facture.

Chaque travail est caractérisé par :


un numéro, l’identification du mécanicien qui a effectué le travail, une description, un temps
alloué, la date et l’heure de début, la date et l’heure de fin, un tarif horaire (ce tarif change
très souvent).
Les différents travaux d’OR peuvent être effectués par différents mécaniciens. Un OR
nécessite généralement l’acquisition des pièces stockées au magasin.
Chaque pièce est caractérisée par un code, une description et un prix unitaire.
Il est évident que la quantité exacte en stock de chaque pièce doit toujours être connue. La
facture peut comprendre la fourniture des produits consommables (huiles, carburant, produits
de nettoyage,…).

Dès que le chef d’atelier accepte un véhicule en réparation, ce véhicule est directement mis
sur 1 des 10 emplacements pour établissement de la liste des pièces à remplacer.
Si la pièce demandée est en magasin, le magasinier effectue une acquisition immédiate, sinon
un traitement d’acquisition différée de la pièce est nécessaire.
Lorsque toutes les pièces sont prêtes, le mécanicien désigné se charge du travail et prévient le
chef lorsque le travail est terminé.
Un OR reste ouvert et consultable jusqu’à la fin des travaux.

Il est demandé :

a. d’appliquer une démarche de découpe de projet sur ce projet,


b. d’analyser chaque composant issu de ce projet afin d’en définir les données (sous forme
de classes ou de lots d’informations (fichiers ou bases de données)),
c. de construire une architecture logicielle correcte de tout le projet,
d. de déduire une architecture modulaire de cette architecture logicielle,

140
e. de développer, selon une méthodologie adéquate, un module ou un composant qui est sous
le contrôle ou sous la responsabilité du département de «magasin des pièces»,
f. de développer, selon une méthodologie adéquate, un module ou un composant qui est sous
le contrôle ou sous la responsabilité du département des «services administratifs»,
g. de développer, selon une méthodologie adéquate, un module ou un composant qui est sous
le contrôle ou sous la responsabilité du département
d’ «atelier d’entretien et de réparation des véhicules».

13. Déterminez quelques situations intermédiaires et l’invariant relatifs à la boucle la plus


intérieure du dernier algorithme de l’atelier 1.

14. Soit le module suivant :


début
s 0
j i
n i + 10
tant que j ≤ n faire s s+j
j j+1

écrire s
renvoyer s
fin

Question 1 : Quelle est l’interface de ce module ?

Question 2 : En utilisant la trace formelle ou toute autre technique de déduction, quelle


est sa spécification ?

Question 3 : Quelle est une cohésion possible de ce module si les composants (chacun
avec sa sous-spécification) en sont les suivants :

COMP1 : s 0
j i sous-spécification : initialisation des variables
n i + 10 internes et externes

COMP2 : tant que j ≤ n faire s s+j sous-spécification :


j j+1 calcul de la somme s

COMP3 : écrire s sous-spécification :


renvoyer s renvoi de s.

Question 4 : Selon la découpe en composants mentionnés à la question 3, une cohésion


séquentielle est-elle possible pour ce module ?

R :  Non, une cohésion séquentielle n’est pas possible, car COMP3 ne


transforme pas (n’enrichit pas) s

141
Question 5 : Etudiez, analysez et dessinez le flux de la découpe de ce module en
les composant suivants :

COMP1 : s 0

COMP2 : j i
n i + 10
tant que j ≤ n faire s s+j
j j+1

COMP3 : écrire s
renvoyer s

Question 6 : Essayez de déduire une cohésion fonctionnelle de ce module.

Remarques importantes :
1. ce module doit contenir au moins 2 composants car on ne peut pas
mélanger un calcul interne et un travail d’I/O dans un seul composant,
2. on ne peut pas mélanger 2 calculs internes différents sein d’un seul
composant.
3. on peut mélanger 1 calcul avec ses initialisations correspondantes au sein
d’un seul composant.

Question 7 : Spécifiez la structure itérative en question.

15. S’il est demandé de spécifier par assertions un algorithme qui calcule la factorielle d’un
nombre entier donné venant de l’environnement (du clavier, p. ex),
analysez la réponse suivante :
E= Φ
D = valeur_entrée ∈ N
PRE = ( 1 ≤ valeur_entrée ≤ 7)
INV = Φ
S= Φ
I = valeur_sortie ∈ N
POST = (valeur_sortie = valeur_entrée !)

16. S’il est demandé de spécifier par assertions un algorithme qui calcule la factorielle d’un
nombre entier donné venant de l’environnement (du clavier, p. ex),
analysez la réponse suivante :
E= Φ
D = valeur_entrée ∈ N
PRE = Φ
INV = Φ
S= Φ
I = valeur_sortie ∈ N
POST = (valeur_entrée ≥ 1 ∧ valeur_entrée ≤ 7  valeur_sortie = valeur_entrée !
V
valeur_entrée < 1 ∨ valeur_entrée > 7  valeur_sortie = ‘calcul impossible’)

142
17. Corrigez la contradiction dans la spécification de l’exercice 16.

18. S’il est demandé de spécifier par assertions un algorithme qui calcule la factorielle d’un
nombre entier donné venant de l’environnement (du clavier, p. ex),
analysez la réponse suivante :
E= Φ
D = valeur_entrée ∈ Z
PRE = Φ
INV = Φ
S= Φ
I = valeur_sortie ∈ N
POST = (valeur_sortie = valeur_entrée !)

19. S’il est demandé de spécifier par assertions un algorithme qui calcule la factorielle d’un
nombre entier v donné qui ne vient pas de l’environnement,
analysez la réponse suivante :
E=v
D = v∈ N
PRE = (1 ≤ v ≤ 7)
INV = Φ
S= Φ
I = t∈ N
POST = (t = v !)

20. Si i, b ∈ N et q, a ∈ R, spécifiez l’algorithme suivant par assertions :


début
i 0
q 0
tant que i < b faire q q + a
i i+1
fin

21. Soit l’algorithme suivant :


début
lire a
k v
j 1
p 0
tant que j ≤ k faire p p + bj
j j+1
p p mod a
écrire p
fin

a. quelles sont son interface et sa spécification ?


b. déterminez les 4 premières situations intermédiaires de sa boucle,
c. dérivez une situation invariante et un invariant de cette boucle,
d. proposez une découpe en composants et déterminez-en une cohésion interne possible.

143
22. Soit l’algorithme suivant :
début
j 1
tant que j ≤ n faire a[j] b[j] + k
j j+2

i 2
tant que i ≤ n faire a[i] b[i] - w
i i+2
fin

a. quelles sont son interface et sa spécification ?


b. déterminez les 4 premières situations intermédiaires de la 1ère boucle,
c. dérivez une situation invariante et un invariant de cette 1ère boucle,
d. déterminez les 4 premières situations intermédiaires de la 2ème boucle,
e. dérivez une situation invariante et un invariant de cette 2ème boucle,
f. proposez une découpe en composants et déterminez-en une cohésion interne possible.

23. Spécifiez par assertions un algorithme qui accepte de l’environnement 3 valeurs réelles et
renvoie leur maximum.

24. Spécifiez par assertion un algorithme qui récupère dans la variable d (destiné à un autre
module) le maximum des 3 nombres réels non nuls que cet algorithme trouve dans les
variables a, b et c.

25.Spécifiez par assertions un algorithme qui calcule les racines d’une équation de second degré
ax2 + b x + c = 0 en sachant que les valeurs réelles a, b et c sont déjà connues au lancement
de cet algorithme.

26. Pour la question 25 ci-dessus, analysez la réponse suivante :


E = {a, b, c}
D = a € R, b € R, c € R
PRE = Ǿ
INV = Ǿ
S = x1, x2, y
I = x1 € R, x2 € R, y € STRING
POST = ((b2-4ac > 0  ax12 + bx1 + c = 0 Λ ax22 + bx2 + c = 0) V
(b2-4ac = 0  x1=x2 Λ ax12 + bx1 + c = 0) V
(b2-4ac < 0  y=’calcul impossible’))

27. Spécifiez, par type abstrait, un module de traitement des clients d’une banque étant donné
que :
ci constitue la suite des informations relatives au compte du client (i étant l’identification du
client i) et COMPTE la structure de données (objet, type d’articles, ensemble d’articles,
fichier, base de données, … ) contenant tous les ci.

28. Spécifiez par assertions un algorithme qui trie le tableau a[1..n] d’entiers en ordre croissant.
Ce tableau vient de l’environnement et le résultat va aussi vers l’environnement.

144
29. Spécifiez par assertions un programme qui renvoie toutes les positions du caractère «a»
au sein du string x.

30. On dispose d’un fichier séquentiel appelé ELEVE dans lequel chaque lot d’information est
composé du numéro identifiant de l’élève (num), du nom de l’élève (nom) et de la
promotion de l’élève (prom).
Il est demandé de spécifier par assertions l’algorithme qui doit trier ce fichier, en ordre
croissant sur base de num dans un 2ème fichier séquentiel appelé ELEVE’.

31. Spécifiez par assertions un module qui met à jour un stock d’articles lors d’une vente sur
base du numéro de l’article vendu (num) et de la quantité vendue (qt)

32. Spécifiez par assertions l’algorithme suivant :


début
t 0
i 0
tant que i < n faire i i+1
t t+i
fin

33. Si a et b € R, donnez la spécification concrète par assertions de l’algorithme suivant :


début
a a+b
b a–b
a a–b
fin

34. Au sein de l’alphabet français, considérons une suite finie w de caractères et 2 autres
caractères x et y venant tous de l’environnement.
Il est demandé de spécifier par assertions un module doit :
- remplacer dans la suite finie w tout caractère x par le caractère y,
- compter dans la variable d le nombre de fois que ce remplacement a eu lieu.

35. Soit une suite finie w de caractères d’alphabet français venant d’un autre module, il est
demandé de spécifier par assertions un module qui affecte dans la variable x l’inverse (la
transposée) de w.
Ex : transposée de «banane» est : «enanab»,

36. Soient 2 chaînes de caractères quelconques w = ai ai+1 ………… ak et


x = bjbj+1……….. bp,
il est demandé de spécifier par assertions un algorithme qui compte le nombre de fois que la
chaîne x apparaît complètement dans la chaîne w.

145
37. Avec a € N et b € N, spécifiez par assertions l’algorithme suivant :
début
i 0
t a
tant que t ≥ b faire t t-b
i i+1
fin

38. Un hôpital doit, chaque jour, fournir le nombre de tous les patients qui consomment un
régime alimentaire donné. Spécifiez par assertions ce composant.

39. Spécifiez par assertions un module qui fournit la somme de tous les nombres premiers ≤ n.

40. L’exercice qui suit permettra :


- de cibler ou de redéfinir correctement les points à améliorer au sein d’une organisation,
- d’intégrer correctement les notions de :
• projet informatique,
• premières décompositions d’un projet,
• premières formes de spécifications (spécifications informelles),
• dossier de développement.

Dans un magasin d’achat et de vente d’articles d’habillement nommé ZABULON, un client


dépose directement sa commande (accompagnée éventuellement d’un chèque) à la réception.
Cette commande est satisfaite immédiatement à concurrence de la disponibilité des stocks.
On estime cependant que 50 % des commandes ne peuvent pas être satisfaites totalement.
Dans ce cas, il y a alors une première livraison qui sera suivie d’une ou de plusieurs livraisons
différées pour solder une commande.
Les frais de transport et les frais administratifs accrus relatifs à ces livraisons différées ne sont
pas facturés.
Dans le cas où une commande doit subir des livraisons différées, le client concerné est averti.
Pour la constitution du colis d’un client, un employé parcourt les 3800 boîtes de rangement
étalées sur plusieurs étagères dans le dépôt de ZABULON, ce parcours est parfois une source
d’erreur et cause souvent un gaspillage de temps.
A la fin de la constitution du colis, c’est ce même employé qui s’occupe de l’expédition du
colis.
Un inventaire de gestion est produit manuellement et périodiquement sur des fiches pré-
imprimées pour aider divers responsables notamment le chef du dépôt à coordonner ses
activités internes.

Les responsables de ZABULON désirent automatiser la gestion de ce magasin en vue de


diminuer le nombre des livraisons différées et celui de ventes perdues, surtout que ses recettes
sont très précaires.

En s’appuyant sur des éléments de l’AF (Modèles de données, de traitement et contraintes),


répondez aux questions ci-après (les réponses qui les accompagnent ne sont pas les seules).

146
La notion de projet a beaucoup évolué, et il n’existe pas de critères précis d’identification de
projet. Toutefois, par définition, un projet :
- implique une grande transversalité de domaines d’expertise (rencontre et collaboration des
connaissances diverses),
- est associé à des objectifs opérationnels, innovants ou non, à atteindre dans un ou plusieurs
domaines d’une organisation et en des délais et des coûts prévus,
- est décomposable itérativement jusqu’en des tâches fines maîtrisables.

Pratiquement, un projet est décomposable en applications. Chaque application est


décomposable en phases (responsabilités). Les applications ne se communiquent
qu’occassionnellement car il s’agit des processus quasi-autonomes.
Une phase possède des ressources bien identifiées, une identité spatio-temporelle, se
déroule de manière ininterruptible (au point de vue décision) et doit pouvoir être
décomposée de façon élémentaire en fonctionnalités (sous-ensembles de règles de traitement)
qui ont des résultats parfaitement identifiées sur la mémoire du SI (système d’information).

Question 1 : Quels sont les objectifs opérationnels (ou organisationnels, càd de métier)
décelez-vous au sein de ZABULON ?
R:
Le texte qui décrit la situation de ZABULON est un résumé qui ne contient pas les tous
détails nécessaires pour appréhender toute sa réalité et dont on a enlevé tous les synonymes,
les homonymes et les éléments contradictoires.
Cela est déjà un travail important sur la voie de la formalisation.
En pratique, la situation à appréhender sera généralement complétée, à l’aide des interviews
notamment.

Pour bien décrire les objectifs opérationnels, il faut :


1. trouver un verbe et des compléments pour caractériser globalement le problème
opérationnel ou organisationnel.
Pour ZABULON, l’objectif opérationnel est d’améliorer la gestion des stocks de ses
articles d’habillement.
Il est possible d’aboutir à plusieurs objectifs opérationnels. Dans ce cas, on peut les traiter
dans un projet unique d’automatisation s’ils s’apparentent ou si l’un est inclus dans l’autre,
sinon, il est conseillé de distinguer plusieurs projets d’automatisation.

2. apporter des réponses au problème opérationnel en termes d’assertions vérifiables ou


déductibles dans la situation existante de ZABULON. Ces assertions permettent en fait de
décomposer l’objectif opérationnel.
Considérons, par exemple, les 4 assertions suivantes :
- tous les acteurs impliqués dans la gestion des stocks de ces articles d’habillement doivent,
à tout moment, connaître l’état réel es stocks (apparemment, la «gestion des achats» n’est
pas avertie à temps que les quantités en stocks ont atteint leurs points critiques),
- supprimer toute source de désaccord entre «la gestion des ventes» et la
«gestion des achats» en les mettant au même niveau fonctionnel (automatiser toutes les 2
gestions),
- produire automatiquement l’inventaire de gestion et diminuer sa périodicité,
- optimiser le parcours de l’employé qui constitue les colis.

147
Question 2 : Quels sont les objectifs informationnels pour atteindre les besoins exprimés
généralement par les organisations ?
R:
Précision de l’information : qualité d’une information qui ne peut admettre rien d’aléatoire ni
de superflu par rapport à ses 3 paramètres essentiels dans le réel
perçu
(temps : information dépassée ?
provenance : à chaque moment peut on être sûr de la provenance
d’une information,
intégrité : l’information a-t-elle modifiée ?),
Vitesse de l’information : qualité d’une information à être livrée rapidement à qui de droit.
Elle dépend de la qualité des processus, des processeurs et des
supports de transmission,
Age de l’information : cette variable exprime la longueur de temps entre le moment où le
phénomène qui a généré l’information a eu lieu et le moment où elle a
été captée dans la mémoire du système d’information,
Horizon temporel de l’information : cette variable est relative au temps d’accessibilité ou
de visibilité de l’information par les processus ou les
les processeurs du système d’information,
Ex : visibilité de données historiques (backups),
Niveau d’agregation de l’information : cette variable est relative à la présentation finale de
l’information en permettant qu’elle soit agrégée ou
spécifique et détaillée.
Ainsi, à un certain niveau de décision (d’organisation),
l’information qui était qualitative et non spécifique
doit, à un autre niveau de décision, devenir quantitative
et spécifique (et vice versa),
Ex : note de cotation des élèves,
Diffusion de l’information : qualité d’une information à transiter rapidement sur des supports
de transmission,
Efficacité de l’information : qualité d’une information qui produit des résultats attendus de
manière rentable et efficace.

148
Question 3 : Déduisons les objectifs informationnels propres à ZABULON de son objectif
opérationnel ou organisationnel.
R : Il est important de savoir que ce sont les objectifs informationnels qui doivent être déduits
de l’ (des) objectif(s) opérationnel(s) et non le contraire.
La décomposition de l’objectif opérationnel (cfr question 1) aidera les concepteurs à
trouver des objectifs informationnels spécifiques et les périmètres d’action nécessaires.

Ainsi, pour ZABULON, on peut déduire, par exemple, ce qui suit :


- pour que tous les acteurs impliqués dans la gestion des stocks des articles d’habillement
connaissent, à tout moment, l’état réel des stocks,
augmenter vitesse de la mise-à-jour des stocks,
diminuer âge de la modification des stocks
périmètre : services qui autorisent la sortie et l’entrée des articles,

- pour supprimer toute source de désaccord entre «la gestion des ventes» et la
«gestion de Achats»,
augmenter diffusion, vitesse des agrégats,
périmètre : services qui autorisent la sortie et l’entrée des articles et qui font la
jonction entre les 2,
- pour produire automatiquement l’inventaire de gestion et diminuer sa périodicité
améliorer horizon temporel, vitesse, âge de cette production,
périmètre : service de gestion des stocks,

- pour optimiser le parcours de l’employé qui constitue les colis


augmenter efficacité de ce parcours,
périmètre : services de livraison, services qui autorisent la sortie des articles.

L’objectif opérationnel d’améliorer la gestion des stocks de ses articles d’habillement.


est finalement un projet décomposable en au moins 2 applications :
«Gestion des achats» et «Gestion des ventes».
L’application «Gestion des ventes» peut être décomposée en les phases suivantes :
* enregistrement de bon de commande,
* préparation de bon de commande,
* mise-à-jour du stock,
* ordonnancement,
* parcours du dépôt,
* constitution du colis,
* expédition de livraison,
* sélection des commandes différées,
* avertissement de non-livraison totale, …

Les fonctionnalités de la phase «enregistrement de bon de commande » peuvent être :


- vérification de l’identité du client,
- vérification du niveau de stock des articles commandés,
- enregistrement effectif de la commande, ….
Une des fonctionnalités de la phase «mise-à-jour du stock» serait :
«production d’un inventaire de gestion».

Les thèmes des applications, des phases et des fonctionnalités fournissent des éléments
solides à la formulation des premières spécifications.

149
Question 4 : Quels sont les éléments de base d’un dossier de développement (cahier de
charges technique) ?
R : La réussite d’un projet passe nécessairement par une bonne documentation, la rédaction
détaillée d’un dossier de Développement (cahier de charges technique) définira de manière
précise les éléments vitaux de développement.

Le dossier de développement contiendra les éléments suivants :

1. Présentation de la solution globale retenue dans l’avant-cadre (projet-cadre) :


- rappel ou redéfinition des objectifs globaux,
- spécification des modèles de données et de traitement (classes UML, MCD, …),

2. Description des Fonctionnalités et des Responsabilités :


- définition des fonctionnalités principales (au point de vue « métiers ») et auxiliaires,
- détermination des responsabilités (regroupement de fonctionnalités),
- proposition des menus et des choix ergonomiques,

3. Description des méthodologies, démarches et étapes de conception


Pour chaque sous-ensemble élémentaire (sans transversalité) qui doit avoir des
répercussions visibles dans la mémoire du SI :
- explication des théories utilisées,
- explication des étapes et modèles utilisés,

4. Description de l’architecture logicielle :


- identification des relations entre composants,
- identification précise des flux internes et externes de chaque composant, et de toute
l’architecture.

5. Contour technique et environnemental :


- description des données et de leur organisation,
- spécification des systèmes et des infrastructures matérielles,
- spécification des technologies,
- spécification des performances attendues (temps et espaces),

6. Spécification d’Acceptation et de Contrôle :


- description des jeux de test et /ou de validation,
- expression des conditions de maintenance et d’évolution de l’environnement,

7. Description des contraintes et des qualités du projet :


- contraintes de coûts (alloués ou à allouer au projet),
- contraintes de délais.

150
41. Fournissons une architecture logicielle OOD basée sur une découpe sous forme de
paquettages et des classes qui peut servir de modèle pour implémenter les objets et les
règles du jeu d’échecs.

Hint : En usant de la notion d’application (1ère subdivision d’un problème proposée ci-
avant), on peut relever dans l’environnement du jeu d’échecs (comme d’ailleurs
dans la plupart des jeux) essentiellement les 4 applications suivantes :
1. spécification de la structure physique du jeu,
2. spécification des éléments de jeu (pièces) de chaque joueur,
3. spécification du positionnement et du déplacement des pièces,
4. spécification du déroulement d’une partie.

42. Le magasin ZABULON dont on a parlé ci-avant a conclu des partenariats avec plusieurs
maisons de confection d’habits divers.
Une de ces maisons, maison DORCAS, qui dispose de 20 couturiers et de 80 machines à
coudre, à boutonner et à broder souhaite informatiser une partie de sa gestion.
Au sein de cette maison, chaque couturier, qui est caractérisé par un identifiant interne, un
nom, une adresse et une qualification, utilise de 1 à 10 machines selon sa qualification.
Une qualification peut être : S0, S1, S2, S3, S4 ou S5
(S0 est équivalent à «sans qualification») .
Chaque machine constitue un poste de travail et est dotée d’un numéro, d’une localisation et
d’un code d’accès qui est connu des seuls couturiers qui y ont accès.
Chaque habit produit est caractérisé par un code, un nom de modèle, une date de début de
confection, une date de fin de confection et un type de tissu.
En outre, on souhaite connaître, à tout moment :
pour un pantalon, le nombre de porte-ceintures,
pour une chemise, le type de col et le type de manches,
pour une robe, la taille de contour de poitrine et le type de manches.

Chaque habit confectionné demande d’être pris successivement en charge par au plus 7 postes
de travail différents et peut nécessiter un certain nombre d’accessoires qui sont gardés dans 16
armoires bien identifiées chacune par un numéro, un code d’accès et une couleur.
Un accessoire est repéré par un type.
Chaque couturier a accès à au plus 3 armoires d’accessoires.

Il est demandé de :
- fournir une architecture logicielle OOD (paquettages et diagramme de classes) qui peut
servir à implémenter correctement cette gestion,
- mettre chacune des responsabilités ci-dessous dans une ou plusieurs classes (en explicitant la
décision) et de l’implémenter :
1. modifier l’adresse de couturiers qui ont utilisé une machine à broder pour la confection
de toutes les robes qui se sont achevées avant une date donnée,
2. lister toutes les chemises qui ont utilisé le même type de tissu et qui ont sollicité
l’intervention d’un couturier qui a accès à 2 armoires données,
3. lister les chemises dont la confection n’est pas encore finie, qui n’ont pas encore utilisé de
machine à boutonner ni aucun accessoire,
4. autoriser un couturier qui a déjà participé à la confection d’une chemise avec un type de
col donné à accéder à une machine déterminée,
5. compter le nombre d’accessoires utilisés entre 2 dates données pour des robes d’un type
de manches donné et qui ont nécessité un couturier d’une qualification donnée,

151
6. modifier le code d’accès des postes de travail qui ont participé entre 2 dates données à la
confection d’au moins de 2 robes d’un type de tissu donné,
7. renvoyer le nombre de robes confectionnées avant une date donnée et ayant une taille de
contour de poitrine > 90 et ayant utilisé un accessoire donné,
8. renvoyer le nombre de chemises, de robes et de pantalons confectionnés entre 2 dates
données et qui ont nécessité l’accès à une armoire donnée,
9. renvoyer le nombre de pantalons confectionnés après une date donnée, par des couturiers
dépourvus d’une qualification donnée et qui ont accès à une armoire déterminée,
10. modifier la localisation de toutes les machines qui n’ont participé à aucune confection
d’habit depuis une date donnée et qui sont accessibles par des couturiers habitant à la
Ruashi,

11. lister les localisations des armoires qui ont fourni les accessoires utilisés pour toutes les
chemises qui n’ont nécessité aucune qualification de la part de tous les couturiers ,
12. fournir le nombre de machines à boutonner d’une localisation donnée qui ont participé à la
confection des pantalons avant une date donnée,
13. modifier le code d’accès de toutes les machines à broder qui ont sollicité une armoire
donnée après une date déterminée,
14. compter le nombre de robes qui ont utilisé des machines à broder accessibles par un
couturier donné et qui ont consommé plus de 5 accessoires,
15. modifier la qualification de couturiers qui accèdent à une armoire de couleur rouge et dont
le nombre d’accessoires est > 12,
16. lister les numéros de postes de travail accessibles à des couturiers sans qualification et qui
ont accès à 1 seule armoire,
17. autoriser un couturier donné à accéder à une armoire qui contient au moins 2 accessoires
d’un type donné ayant déjà été utilisés pour la confection des robes,
18. lister les codes des machines à coudre qui ont la même localisation et qui peuvent être
utilisées par des couturiers qui n’ont accès à aucune armoire,
19. lister et modifier les codes d’accès de toutes les machines à boutonner qui ont participé
à des confections de chemises ayant nécessité des accessoires d’une armoire donnée,
20. modifier les codes d’accès de toutes les armoires accessibles par des couturiers qui
participent à des confections de pantalons qui ne sont pas encore terminés,
21. lister tous les habits qui n’ont pas utilisé de machines à broder et qui ont utilisé le même
type de tissu entre 2 dates données.
22. fournir les numéros de postes de travail qui ont participé à la confection d’un pantalon
ayant nécessité l’accès à au moins 5 armoires différentes,
23. compter le nombre de pantalons produits avant une date donnée et qui n’ont pas utilisé un
ensemble donné d’accessoires.

152
R : On peut dénombrer logiquement 5 paquettages (applications ou composants de base) :
1. spécification des machines,
2. spécification des habits,
3. spécification des accessoires,
4. spécification des armoires,
5. spécification des couturiers.
Ce qui pourrait fournir le diagramme de classes suivant :

Machine à Machine à Machine à Pantalon Chemise Robe


Boutonner Coudre Broder

Machine Habit
1..7 0..*

1..3

1
Couturier

1..*

0..3
Armoire Accessoire
1..1 0..*

153
43. On dispose, à l’entrée, de 2 fichiers : JOURNAL et ARCHIVAGE qui ont des
structures différentes, mais qui ont la même zone de date (DOP).
Ces 2 fichiers sont triés en ordre croissant sur DOP.

Il est demandé, en développement structurel, de produire un 3ème fichier (appelé


GLOBAL) qui contiendra, sur base de DOP, des extraits de JOURNAL et
d’ARCHIVAGE selon une fonction particulière h choisie :
- si h = «extraction» alors GLOBAL = (groupes concordants d’ARCHIVE)*,
- si h = «extraction-insertion» alors GLOBAL = (groupes concordants d’ARCHIVE +
groupes concordants de JOURNAL)*,
- si h = «nettoyage» alors GLOBAL = (groupes non-concordants d’ARCHIVE)*,
- si h = «remplacement» alors GLOBAL = (groupes non-concordants d’ARCHIVE +
groupes non-concordants de JOURNAL)*,
- si h = «fusion» alors GLOBAL = (groupes concordants d’ARCHIVE +
groupes de JOURNAL)*.
Il est à noter qu’il n’y a aucun ordre à respecter, au sein du fichier GLOBAL, entre les
enregistrements de JOURNAL et ceux d’ARCHIVE.

Définitions et suggestions rapides :

Soient D = (d)* = {a1, a2, …………, an}


E = (e)* = {e1, e2, …………., em},
avec ai et ej étant des occurrences quelconques.
Soient 2 groupes G ⊆ D et H ⊆ E tels que
G = {ai, …, aj}, 1 ≤ i ≤ j ≤ n et ayant la même valeur d’indice v
(ai-1, s’il existe a une autre valeur d’indice et,
aj+1, s’il existe a une autre valeur d’indice),
H = {ek, …, ep}, 1 ≤ k ≤ p ≤ m et ayant la même valeur d’indice w
(ek-1, s’il existe a une autre valeur d’indice et,
ap+1, s’il existe a une autre valeur d’indice).
G et H sont dits groupes concordants si v = w sinon ils sont dits groupes non-concordants.

Si les valeurs de DOP les 2 fichiers JOURNAL et ARCHIVAGE se présentent comme


suit :
1, 1, 3, 5, 9, 9, 9, 12 (8 enregistrements dans JOURNAL),
0, 1, 5, 7, 7, 9, 9, 9, 9, 10, 14 (11 enregistrements dans ARCHIVE)
alors il y a 3 paires de groupes concordants composés de :
3 «1» (2 enregistrements de JOURNAL et 1 enregistrement de ARCHIVE),
2 «5» (1 enregistrement de JOURNAL et 1 enregistrement de ARCHIVE),
7 «9» (3 enregistrements de JOURNAL et 4 enregistrements de ARCHIVE).

154
R:
1. Diagramme de réseau du système

Le DRS se présente comme suit :

Fichier
ARCHIVE trié
PRODUCTION des
Fichier GLOBAL
enregistrements

Fichier
JOURNAL trié

2. Définition et vérification des structures de données

Le fichier d’input ARCHIVE est une itération de ‘groupes archive’,


un groupe archive est une sélection de ‘groupes concordants archive’ ou
de ‘groupes non-concordants archive’,
un groupe concordant archive est une itération de ‘enregistrements archive’,
un groupe non-concordant archive est une itération de ‘enregistrements archive’.

Le fichier d’input JOURNAL est une itération de ‘groupes journal’,


un groupe journal est une sélection de ‘groupes concordants journal’ ou
de ‘groupes non-concordants journal’,
un groupe concordant journal est une itération de ‘enregistrements journal’,
un groupe non-concordant journal est une itération de ‘enregistrements journal’.

Le fichier d’output GLOBAL est une itération de ‘groupes global’,


un groupe global est une sélection de «extraction», de «extraction-insertion»,
de «nettoyage» , de «remplacement» ou de «fusion»,
«extraction» est une itération de «groupes concordants archive»,
«extraction-insertion» est une itération de cc,
cc est une séquence d’un «groupe concordant archive» et
d’un «groupe concordant journal»,
«nettoyage» est une itération de «groupes non-concordants archive»,
«remplacement» est une itération de nn,
nn est une sélection d’un «groupe non-concordant archive» ou
d’un «groupe non-concordant journal»,
«fusion» est une itération de nc,
nc est une sélection d’un «groupe concordant archive» et
d’un «groupe journal».

155
3. Définition d’une structure du problème
Après vérification de toutes les structures et de toutes les correspondances, on peut
aboutir à la structure du problème décrite à l’Annexe A (où les feuilles constituées
essentiellement d’enregistrements archive, d’enregistrements journal et
d’enregistrements global.
On voit apparaître sur ce schéma un des phénomènes (naturels) de télescopage signalé
dans le développement fonctionnel et dans le développement structurel :
plusieurs éléments à l’input correspondent à un même élément à l’output.

Toutefois, les correspondances différentes de 1-1 risquent d’accroître le degré de


couplage entre les divers composants.
A ce niveau et en fonction de la quantité de ces phénomènes de télescopage, le
concepteur peut décider de continuer avec les principes du développement structurel,
mais la structure obtenue devrait être vérifiée et examinée soigneusement afin de
résoudre manuellement (par la génération des structures intermédiaires ou autres) les
disfonctionnements éventuels qui résulteraient de ces correspondances sans trop sacrifier
la structure acquise.

4. Déduction d’une Structure de solution


Si nous optons de poursuivre avec le développement structurel, nous déduisons la
structure de solution décrite à l’Annexe B.

5. Dérivation et allocation des composants et opérations élémentaires

a. Production des algorithmes et analyse de la structure


L’algorithme issu du dernier niveau de la structure décrite à l’Annexe B est :

début
tant que C0 faire

si h = extraction alors
si C11 alors tant que C1 faire consommer Gca pour produire Gca (1)

si h = extraction-insertion alors
tant que C2 faire si C22 alors consommer Gca pour produire Gca
sinon consommer Gcj pour produire Gcj (2)

si h = nettoyage alors
si C33 alors tant que C3 faire consommer Gnca pour produire Gnca (3)

si h = remplacement alors
tant que C4 faire si C44 alors consommer Gnca pour produire Gnca
sinon consommer Gncj pour produire Gncj (4)

si h = fusion alors
tant que C5 faire si C55 alors consommer Gca pour produire Gca
sinon consommer Gj pour produire Gj (5)
fin

156
A cause des phénomènes de télescopage ci-avant, il importe surtout de bien réfléchir
sur les différentes conditions (récurrence et tests).
Puisqu’il s’agit de concordance de groupe d’enregistrements, il faudra théoriquement
distinguer, pour chaque fonction, les 3 cas (exclusifs) possibles suivants :

- cas DOP (ARCHIVE) = DOP (JOURNAL),


cas où les enregistrements concordants du fichier ARCHIVE et du fichier
JOURNAL sont disponibles,

- cas DOP (ARCHIVE) < DOP (JOURNAL),


cas où les enregistrements non-concordants du fichier ARCHIVE sont disponibles,

- cas DOP (ARCHIVE) > DOP (JOURNAL),


cas où les enregistrements non-concordants du fichier JOURNAL sont disponibles.

b. Définition et allocation des opérations élémentaires

Après analyse, la condition C11 est celle qui exprime les 3 cas ci-dessus et
C1 est «DOP (ARCHIVE) = DOP (JOURNAL)»,
Cas pour lequel il faut : - copier Gca dans GLOBAL (*), et
- ne pas considérer Gcj (**).
Dans le cas «DOP (ARCHIVE) ≠ DOP (JOURNAL)», il suffit de ne pas considérer
les Gnca et les Gncj (***),
(1) ci-dessus devient alors :

si h = extraction alors
si DOP (ARCHIVE) = DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)

tant que not-end-of-file (ARCHIVE) et


DOP (ARCHIVE) = DOP (JOURNAL) faire copier Gca dans GLOBAL
lire ARCHIVE (*)

si DOP (ARCHIVE) > DOP (JOURNAL) alors


tant que not-end-of-file (JOURNAL) et
DOP (JOURNAL) = sDOP faire lire JOURNAL (**)
----------------------------------------------------------
si DOP (ARCHIVE) < DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)
tant que not-end-of-file (ARCHIVE) et
DOP (ARCHIVE) = sDOP faire lire ARCHIVE (***)
----------------------------------------------------------
si DOP (ARCHIVE) > DOP (JOURNAL) alors
sDOP DOP (JOURNAL)
tant que not-end-of-file (JOURNAL) et
DOP (JOURNAL) = sDOP faire lire JOURNAL (***)

157
En examinant de près les conditions C2 et C22, on peut vite se rendre compte qu’elles
ne sont pas dans le bon ordre,
à cause notamment des phénomènes de télescopage signalés ci-avant, l’ordre du «si»
et du «tant que» a été perturbé.
Le «si» devrait précéder le «tant que», le bon ordre est donc :

si C22 alors tant que C2 faire consommer Gca pour produire Gca
consommer Gcj pour produire Gcj
sinon …

La condition C22 est celle qui exprime les 3 cas ci-dessus et


C2 est «DOP (ARCHIVE) = DOP (JOURNAL)»,
cas pour lequel il faut copier Gca et Gcj dans GLOBAL (*).
Dans le cas «DOP (ARCHIVE) ≠ DOP (JOURNAL)», il suffit de ne pas considérer
les Gnca et les Gncj (**),
(2) ci-dessus devient alors :

si h = extraction-insertion alors
si DOP (ARCHIVE) = DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)

tant que not-end-of-file (ARCHIVE) et


DOP (ARCHIVE) = sDOP faire copier Gca dans GLOBAL
lire ARCHIVE (*)

tant que not-end-of-file (JOURNAL) et


DOP (JOURNAL) = sDOP faire copier Gcj dans GLOBAL
lire ARCHIVE (*)
----------------------------------------------------------
si DOP (ARCHIVE) < DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)
tant que not-end-of-file (ARCHIVE) et
DOP (ARCHIVE) = sDOP faire lire ARCHIVE (**)
----------------------------------------------------------
si DOP (ARCHIVE) > DOP (JOURNAL) alors
sDOP DOP (JOURNAL)
tant que not-end-of-file (JOURNAL) et
DOP (JOURNAL) = sDOP faire lire JOURNAL (**)

158
Examinons les conditions C3 et C33,
la condition C33 est celle qui exprime les 3 cas ci-dessus et
C3 est «DOP (ARCHIVE) < DOP (JOURNAL)»,
cas pour lequel il faut copier Gnca dans GLOBAL (*).
Dans les cas «DOP (ARCHIVE) = DOP (JOURNAL)» et
«DOP (ARCHIVE) > DOP (JOURNAL)», il suffit de ne pas considérer les Gca
cfr (**),
(3) ci-dessus devient alors :

si h = nettoyage alors
si DOP (ARCHIVE) < DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)

tant que not-end-of-file (ARCHIVE) et


DOP (ARCHIVE) = sDOP faire copier Gnca dans GLOBAL
lire ARCHIVE (*)

----------------------------------------------------------
si DOP (ARCHIVE) = DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)

tant que not-end-of-file (ARCHIVE) et


DOP (ARCHIVE) = sDOP faire lire ARCHIVE
tant que not-end-of-file (JOURNAL) et
DOP (JOURNAL) = sDOP faire lire JOURNAL (**)
----------------------------------------------------------
si DOP (ARCHIVE) > DOP (JOURNAL) alors
sDOP DOP (JOURNAL)
tant que not-end-of-file (JOURNAL) et
DOP (JOURNAL) = sDOP faire lire JOURNAL (**)

159
Les mêmes remarques exprimées pour les conditions C2 et C22 tiennent pour C4 et
C44.
Le «si» devrait précéder le «tant que», le bon ordre est donc :

si C44 alors tant que C4 faire consommer Gnca pour produire Gnca
consommer Gncj pour produire Gncj
sinon …

La condition C44 est celle qui exprime les 3 cas ci-dessus et


C2 exprime le cas «DOP (ARCHIVE) < DOP (JOURNAL)»
et le cas «DOP (ARCHIVE) > DOP (JOURNAL)»,
cas pour lesquels il faut respectivement copier Gnca et Gncj dans GLOBAL (*).
Dans le cas «DOP (ARCHIVE) = DOP (JOURNAL)», il suffit de ne pas considérer
les Gca et les Gcj (**),
(4) ci-dessus devient alors :

si h = remplacement alors
si DOP (ARCHIVE) < DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)

tant que not-end-of-file (ARCHIVE) et


DOP (ARCHIVE) = sDOP faire copier Gnca dans GLOBAL
lire ARCHIVE (*)
----------------------------------------------------------
si DOP (ARCHIVE) > DOP (JOURNAL) alors
sDOP DOP (JOURNAL)

tant que not-end-of-file (JOURNAL) et


DOP (JOURNAL) = sDOP faire copier Gncj dans GLOBAL
lire JOURNAL (*)
----------------------------------------------------------
si DOP (ARCHIVE) = DOP (JOURNAL) alors

sDOP DOP (ARCHIVE)


tant que not-end-of-file (ARCHIVE) et
DOP (ARCHIVE) = sDOP faire lire ARCHIVE

tant que not-end-of-file (JOURNAL) et


DOP (JOURNAL) = sDOP faire lire JOURNAL (**)

160
Les mêmes remarques exprimées pour les conditions C2 et C22 tiennent pour C5 et
C55.
Le «si» devrait précéder le «tant que», le bon ordre est donc :

si C55 alors tant que C5 faire consommer Gca pour produire Gca
consommer Gj pour produire Gj
sinon …

La condition C55 est celle qui exprime les 3 cas ci-dessus et


C5 exprime le cas «DOP (ARCHIVE) = DOP (JOURNAL)»,
cas pour lequel il faut respectivement copier Gca et Gcj dans GLOBAL (*),
le cas «DOP (ARCHIVE) > DOP (JOURNAL)» et le cas
«DOP (ARCHIVE) < DOP (JOURNAL)»,
cas pour lequel il faut copier Gncj dans GLOBAL (*),
(5) ci-dessus devient alors naturellement :

si h = fusion alors
si DOP (ARCHIVE) = DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)

tant que not-end-of-file (ARCHIVE) et


DOP (ARCHIVE) = sDOP faire copier Gca dans GLOBAL
lire ARCHIVE (*)

tant que not-end-of-file (JOURNAL) et


DOP (JOURNAL) = sDOP faire copier Gcj dans GLOBAL
lire ARCHIVE (*)
----------------------------------------------------------
si DOP (ARCHIVE) > DOP (JOURNAL) alors
sDOP DOP (JOURNAL)
tant que not-end-of-file (JOURNAL) et
DOP (JOURNAL) = sDOP faire copier Gncj dans GLOBAL
lire JOURNAL

sDOP DOP (ARCHIVE)


tant que not-end-of-file (ARCHIVE) et
DOP (ARCHIVE) = sDOP faire lire ARCHIVE (*)
----------------------------------------------------------
si DOP (ARCHIVE) < DOP (JOURNAL) alors
sDOP DOP (ARCHIVE)
tant que not-end-of-file (ARCHIVE) et
DOP (ARCHIVE) = sDOP faire lire ARCHIVE
si end-of-file (ARCHIVE) alors
tant que not-end-of-file (JOURNAL) faire copier Gncj dans GLOBAL
lire JOURNAL

161
L’étudiant est invité à compléter cette structure avec les autres composants et opérations
élémentaires utiles (C0, gestion de fins de fichier, demande de fonction, ouverture
et clôture fichiers, couche de présentation, couche de données, …) afin d’implémenter
l’application finale.

Il devra en outre examiner s’il est opportun de considérer le fichier d’output GLOBAL
comme une sélection de «extraction», de «extraction-insertion», de «nettoyage» , de
«remplacement» ou de «fusion» plutôt comme une itération de ‘groupes global’.

162
44. Dans une clinique appelée EPHESE, qui est très fortement fréquentée grâce à l’expertise de
ses médecins, apparaissent des dysfonctionnements fonctionnels qui génèrent d’autres
problèmes de divers types (social, économique,….).

Les dysfonctionnements fonctionnels sont essentiellement :


- difficultés de planifier les consultations car les fiches y relatives ne sont pas toujours à
jour,
- les médecins passent trop de temps sur des tâches administratives (recherche et
classification de fiches, mise à jour des informations de ces fichiers,….), ce qui allonge
inévitablement le temps de service aux malades,
- des traces d’hospitalisation disparaissent,
- gestion inappropriée de suivi de divers services rendus aux malades (services qui accusent
un certain relâchement depuis quelque temps), car ses départements des gestion
(essentiellement la Facturation, les Finances, l’Accueil, la Comptabilité et le Personnel)
et ses départements techniques (essentiellement l’Entretien, la Pharmacie, la Cuisine et
la Buanderie) sont débordés.

Cette clinique comprend actuellement 300 lits d’hospitalisation partagés entre 12 services
médicaux (Maternité, Radiologie, Laboratoire, Kinésithérapie, Pharmacie, Pédiatrie,
Chirurgie, Réanimation, Médecine Interne, Traumatologie, Urologie et Gynécologie…).
Chaque service contient 3 types de chambres :
Chambre à 4 lits, chambre à 2 lits et chambre particulière.
Le nombre de lits dans chaque service varie entre 8 et 24.

Pendant son séjour :


- le patient reçoit un certain nombre de soins, un certain nombre de médicaments et peut
être soumis à un régime alimentaire.
Les prestations, les médicaments et les régimes alimentaires font l’objet d’une prescription
de médecin et sont assurés par les infirmières de service,
- le patient peut, sous autorisation de médecins, changer de service une ou plusieurs fois et
effectuer une sortie temporaire. Il existe 3 espèces de sortie temporaire :
réconfort familial, sortie WE et relâchement.

Après son séjour, le patient reçoit une autorisation de quitter la clinique et libère le lit dans
l’heure qui suit.
Tout patient sorti définitivement depuis 1 mois est considéré comme ancien patient et toutes
ses informations seront archivées.
La clinique accepte 3 modes de paiement : paiement totalement supporté par le malade,
paiement totalement supporté par un ou plusieurs tiers (sociétés, organismes, …) ou
paiement supporté par le malade et par un ou plusieurs tiers (auquel cas il y aura autant
de factures qu’il y a de payeurs).

La gestion de ce projet vous est confiée. On vous demande d’aplanir ces dysfonctionne-
ments fonctionnels en vue d’améliorer et d’innover principalement le circuit administratif,
la gestion des services rendus aux malades et la gestion des facturations.
En outre, l’architecture mise en place devra permettre de mieux partager l’information et
la communication entre les différents acteurs de EPHESE, de réduire la consommation de
papier, les besoins de déplacements internes et les délais des procédures administratives.

163
Cela est l’une des conditions pour que EPHESE entre dans le club prestigieux de «cliniques
d’honneur».

Un des avantages de ce club prestigieux est que EPHESE pourra bénéficier


avantageusement des transferts de certains malades vers d’autres hôpitaux spécialisés
(éparpillés dans le monde entier) affiliés à ce club.

Il est demandé :

a. d’appliquer une démarche de découpe de projet sur ce projet et de proposer toute


hypothèse utile,
b. d’analyser chaque composant issu de ce projet afin d’en définir les données (sous forme
de classes ou de lots d’informations (fichiers ou bases de données)),
c. de construire une architecture logicielle correcte de tout le projet,
d. de déduire une architecture modulaire de cette architecture logicielle,
e. de développer, selon une méthodologie adéquate, les fonctionnalités révélées par
l’analyse et qui sont :
1. consulter l’historique des maladies d’un malade,
2. supprimer un des numéros de téléphone des personnes garantes d’un malade,
3. fournir le nombre total d’un repas donné par service,
4. fournir le nombre total d’un repas donné par jour,
5. fournir une liste ventilée de tous les repas par service,
6. fournir le nombre total de malades qui ont reçu un service donné entre 2 dates données,
7. mettre à jour l’historique d’un malade,
8. gérer l’admission d’un malade à hospitaliser,
9. fournir le nombre total de malades qui n’ont pas reçus un service donné entre 2 dates
données,
10. gérer la fin d’un hospitalisation,
11. mettre à jour des informations sur la médicamentation (interdictions) d’un malade,
12. gérer le transfert de malades d’un service vers un autre,
13. gérer une demande de consultation,
14. gérer une demande d’examen,
15. facturer les services reçus par un patient.
16. mettre à jour des détails relatifs au mode de paiement d’un malade,
17. consulter les examens en attente pour un malade et les raisons d’attente,
18. gérer le suivi des administrations des médicaments,
19. gérer une sortie temporaire de malade,
20. modifier la liste des ingrédients ou aliments interdits pour 1 malade.

164
45. Soit un tableau a[1:n] d’entiers non triés (n≥2).
Il est demandé, par le développement structurel, sans trier ce tableau, d’en renvoyer
(à l’écran) le 2ème plus petit entier et son indice.

46. Soient a[1:n] et b[1:n] deux tableaux d’entiers, il est demandé, par le développement
structurel, de décrire l’architecture logicielle du module qui doit renvoyer :

a. les n éléments issus de la somme de ces 2 tableaux,


b. le produit de tous les éléments d’indice pair de ces 2 tableaux,
c. les éléments issus du produit de ces 2 tableaux,
d. la somme de tous les entiers impairs de ces 2 tableaux,
e. la somme de tous les éléments d’indice impair de ces 2 tableaux,
f. le produit de tous les entiers pairs de ces 2 tableaux,
g. le produit de l’indice du plus petit entier du tableau a et de l’indice du plus grand entier
du tableau b.

47. Etant donné un tableau a[1:n] d’entiers, il est demandé de générer une structure du
programme qui trie ce tableau en ordre croissant, à l’aide des principes du développement
démonstratif.

R : schéma de raisonnement :

- Spécifications du problème
Tous les objets de ce problème sont sous forme formelle et une spécification concrète en
est :

E = (a[1:n])
D = (a[i] ∈ N, i ∈ N, n ∈ N)
PRE = φ
INV = φ
S = (a[1:n])
I = (a[i] ∈ N, i ∈ N, n ∈ N)
POST = (a[i] ≤ a[i+1] ∀ i : 1 ≤ i ≤ n-1)

Tout le programme (qui est à générer) peut donc être vu comme un transformateur S qui
transforme le contexte initial Q (composé de E, D et PRE) en contexte final R (composé de
S, I et POST), càd {Q} S {R}.

- Spécification formelle de R
A partir du contexte final ci-dessus, nous pouvons tirer R tel que :
R = (a[i] ≤ a[i+1] ∀ i : 1 ≤ i ≤ n-1).

De l’expression R, il vient automatiquement que :


Ro = (a[i] ≤ a [j] ∀ i : 1 ≤ i ≤ n-1, ∀ j : i < j ≤ n)

Avant d’atteindre la situation exprimée par Ro, une situation intermédiaire Si possible est :
Si = (a[i] ≤ a [j] ∀ i : 1 ≤ i ≤ k, ∀ j : i < j ≤ n, k < n)

165
Graphiquement :

1 k k+1 n

a[j] ≤ a[j+1] ∀ j : 1 ≤ j < k (R2) a[k+1] ≥ a [j] ∀ j : 1 ≤ j < k (R3)

- Détermination d’un invariant


La situation intermédiaire Si ci-avant est déjà une forme affaiblie du résultat attendu R et nous
pouvons en déduire l’invariant H :
H = (a[i] ≤ a [j] ∀ i : 1 ≤ i ≤ k, ∀ j : i < j ≤ n, k ≤ n).
Nous pouvons noter que :
R ∧ Ro ∧ Si  a[1] ≤ a[j], ∀ j : 1 < j ≤ n ∧
a[2] ≤ a[j], ∀ j : 2 < j ≤ n ∧
a[3] ≤ a[j], ∀ j : 3 < j ≤ n ∧
….
a[k] ≤ a[j], ∀ j : 1 < k ≤ n
L’assertion a[1] ≤ a[j], ∀ j : 1 < j ≤ n suggère que l’élément a[1], pour occuper la 1ère
place du tableau a doit avoir été comparé avec chacun des autres n–1
éléments de ce tableau,
l’assertion a[2] ≤ a[j], ∀ j : 2 < j ≤ n suggère que l’élément a[2], pour occuper la 2ème
place du tableau a doit avoir été comparé avec chacun des autres n–1
éléments de ce tableau,
l’assertion a[3] ≤ a[j], ∀ j : 3 < j ≤ n suggère que l’élément a[3], pour occuper la 3ème
place du tableau a doit avoir été comparé avec chacun des autres n–1
éléments de ce tableau,
….
l’assertion a[k] ≤ a[j], ∀ j : k < j ≤ n suggère que l’élément a[k], pour occuper la kème
place du tableau a doit avoir été comparé avec chacun des autres n–1
éléments de ce tableau.

A la fin, chacun des n éléments du tableau a aura été comparé aux n-1 autres éléments de ce
tableau.
La forme de l’invariant H, où les 2 indices i et j sont universellement quantifiées, et la
déduction R ∧ Ro ∧ Si suggèrent que le programme à générer doit nécessairement avoir 2
itérations imbriquées car les 2 quantifications sont liées par la variable i :
- l’itération la plus extérieure correspond à ∀ i :1 ≤ i ≤ k, vu que c’est celle qui définit la
variable i utilisée dans l’autre itération,
- l’itération la plus intérieure est donc celle relative à ∀ j : i < j ≤ n

Il est important de noter que les éléments présentés jusqu’ici décrivent une caractéristique du
problème posé et ne sont pas attachés à une approche quelconque d’une solution.
On doit aussi noter que ce n’est pas la seule formulation possible.

166
- Dérivation de structure de programme

1. Terminaison des itérations

Pour l’itération la plus extérieure, une fonction de terminaison t qui restera > 0 lorsque
aucune des conditions ne permet de sortir de l’itération et qui va décroître à chaque
récurrence (jusqu’à 0) est : t = (n – 1) – k.

Pour l’itération la plus intérieure, une fonction de terminaison p qui restera > 0 lorsque
aucune des conditions ne permet de sortir de l’itération et qui va décroître à chaque
récurrence (jusqu’à 0) est : p = (n – 1) – i.

La structure de base du programme peut donc être :

début
définir Q
tant que k ≠ n -1 faire
tant que i ≠ n -1 faire
décroître p
décroitre t
R
fin

En analysant Ro, on peut établir que :


- la fin de l’itération la plus intérieure sera caractérisée par la situation suivante :
R1 = (a[i] ≤ a[p] ∀ p : i+1 ≤ p ≤ n)
Un invariant I1 issu de R1 est :
I1 = (a[i] ≤ a[p] ∀ p : i+1 ≤ p ≤ m ∧ m ≤ n)

- la fin de l’itération la plus extérieure sera caractérisée par la situation suivante :


R2 = (a[j] ≤ a[q] ∀ j : 1 ≤ j ≤ n ∧ ∀ q : k+1 ≤ q ≤ n)
Un invariant I2 issu de R2 est :
I2 = (a[j] ≤ a[q] ∀ j : 1 ≤ j ≤ k ∧ ∀ q : k+1 ≤ q ≤ m ∧ m ≤ n ∧ k ≤ n).

La fonction t = (n – 1) – k décroitra naturellement avec l’instruction k k+1, et


la fonction p = (n – 1) – i décroitra naturellement avec l’instruction i i+1.

2. Structure affinée

En tenant compte des assertions R1, I1, R2, I2, de toutes les variables qui lient les itérations
(ici, i) et de l’implication mathématique qui oblige à rétablir un invariant lorsque
l’exécution de l’itération correspondante l’a détruit (eu égard aux variables et objets
contenus dans cet invariant et qui ont été modifiés au cours de l’exécution de l’itération),
on peut aboutir à la structure affinée (structure de base dotée de toutes les assertions de
ses itérations) suivante :

167
début
définir Q
I2
tant que k ≠ n -1 faire rétablir I2
I1
i k
tant que i ≠ n -1 faire
rétablir I1
décroître p

R1
décroitre t

R2
R
fin

Nous pouvons établir ou rétablir I1 = (a[i] ≤ a[p] ∀ p : i+1 ≤ p ≤ m ∧ m ≤ n)


au moyen de l’instruction de sélection
«si a[i] ≤ a[p] alors Ne rien faire sinon Permuter ces 2 éléments».

Nous n’avons rien à faire pour établir ou rétablir


I2 = (a[j] ≤ a[q] ∀ j : 1 ≤ j ≤ k ∧ ∀ q : k+1 ≤ q ≤ m ∧ m ≤ n ∧ k ≤ n)
car I2 est automatiquement vrai lorsque I1 est établi.

Avec les instructions nécessaires à l’établissement et au rétablissement des assertions, nous


aboutissons à l’algorithme suivant :

début
définir Q
I2
tant que k ≠ n -1 faire
I1
i k
tant que i ≠ n -1 faire
si a[i] > a[i+1] alors Les permuter

i i+1

R1
k k+1

R2
R
fin

Pour établir la correction de cet algorithme, il faudra finalement démontrer que :

168
1. Q  I2
2. I2 ∧ (k ≠ n -1)  I2
3. I2 ∧ (k = n -1)  R2 ∧ R
4. (i = k)  I1
5. I1 ∧ (i ≠ n -1)  I1
6. I1 ∧ (i = n -1)  R1.

Ces démonstrations (qui sont laissées à l’attention de l’étudiant) peuvent cristalliser


d’autres instructions élémentaires ou fournir d’autres éclairages sur des séquencements de
certaines instructions.
Il faut souligner que la plupart de démarches par induction sur la structure des traitements
déterminent les instructions de base en fonction des 3 mécanismes de contrôle de flux
mais elles ne fournissent que très peu d’indications sur les séquences d’exécution des
instructions fines du programme à construire.
C’est au concepteur connaissant la logique du problème, qui doit correctement mettre en
séquence les instructions fines du programme (à chaque niveau d’algorithme).

48. Travail Pratique 1 (relatif à l’atelier E1, page 64)

Pour l’ensemble ARTICLE, considérons le jeu de test suivant :

NOM MOUV QUA


1. châle R 50
2. chaussette D 20
3. chaussette R 30
4. chaussette D 2
5. costume D 6
6. écharpe D 2
7. écharpe D 3
8. écharpe D 4
9. foulard R 1
10. foulard R 2
11. foulard R 3
12. foulard R 4
13. justaucorps R 4
14. jupe R 5
15. manteau D 1
16. pantalon R 1
17. pantalon D 1
18. pantalon D 1
19. pantalon R 1
20. vareuse D 100
21. veste D 2
22. veste R 3
23. veste D 2
24. veste R 3
25. veston D 4

169
Partie 1 :
Il est demandé de coder, pour un environnement monoposte et en implémentation
centralisée, dans un langage de votre choix, le dernier algorithme issu du processus de
conception de l’atelier 1 (cfr E1 page 60) et de le tester avec le jeu de test ci-dessus.

Le dossier à remettre spécifiera essentiellement le contour technique en précisant les points


suivants :
1. description des données et de leur organisation (structures, accès, …),
2. description succincte des infrastructures utilisées (technologies, plateformes, …),
3. expression des contraintes sur le respect de la structure dudit algorithme (performances,
contraintes, …),
4. les sources de tous les codes et leur critique (au point de vue de paramètres de qualité).

Partie 2 :
Il est demandé de modifier le code produit (en Partie 1), en ce qui concerne éventuel-
lement ses données, son environnement ou sa présentation, afin qu’il s’intègre dans un
environnement orienté objet, et de tester le nouveau code obtenu avec le même jeu de test,
c-à-d que :
1. les articles reçus et distribués doivent être des objets d’une ou de plusieurs classes à
instancier,
2. les éléments constitutifs de «Produire un inventaire» doivent être dans 1 ou plusieurs
comportements de classe.

Le dossier à remettre spécifiera essentiellement le contour technique en précisant les points


suivants :
1. spécification de chaque classe,
2. explicitation des responsabilités de chaque classe,
3. les sources de tous les codes et leur critique (au point de vue de paramètres de qualité).

Partie 3 :
Il est demandé de modifier les solutions obtenues ci-avant (en Partie 1 et/ou en Partie 2),
afin de proposer une solution Client/Serveur (1/3 au minimum) et de tester la nouvelle
solution obtenue avec le même jeu de test .

Le dossier à remettre spécifiera essentiellement le contour technique en précisant les points


suivants :
1. explicitation des responsabilités du client et du(des) serveur(s),
2. description succincte des infrastructures et technologies utilisées dans l’implémentation
de cette solution Client/Serveur,
3. les sources de tous les codes et leur critique (au point de vue de paramètres de qualité).

170
49. Soient 2 tableaux a[1: n] et b[0:m] tels que 0 ≤ m ≤ n.
Il est demandé d’établir :
i = p si p est la plus petite valeur qui satisfait à la condition a[p:p+m] = b[0:m]
(1 ≤ p, p+m ≤ n),
i = 0 sinon.

R:

L’idée intuitive (sans trop vouloir matérialiser d’emblée les invariants nécessaires) qui
peut dégager une situation générale est la suivante :

a
p p+m n
portion déjà
explorée
b
0 m

Intuitivement, il faudrait donc 2 itérations imbriquées :


- une pour explorer le tableau a,
- une pour exploration de la comparaison des segments a[p:p+m] et b[0:m].

On peut donc, de manière juste, penser que si a[p:p+m] = b[0:m] alors i = p


sinon, il faudra comparer le segment suivant délimité par a[p+1:p+m+1] à b[0:m].

L’idée intuitive de la situation générale exprime l’assertion suivante :


∀ k (1 ≤ k < p) : a[k:k+m] ≠ b[0:m], c-à-d
∀ k (1 ≤ k < p), ∃ j (0 ≤ j ≤ m) : a[k+j] ≠ b[j].
La présence de ∀ et de ∃ dans cette phrase suggère que le programme à générer doit
nécessairement avoir 2 itérations imbriquées et les 2 quantifications sont liées par la
variable k :
- l’itération la plus extérieure correspond à ∀ k (1 ≤ k < p), vu que c’est celle qui
définit la variable k utilisée dans l’autre itération,
- l’itération la plus intérieure est donc celle relative à ∃ j (0 ≤ j ≤ m) : a[k+j] ≠ b[j].

Nous pouvons naturellement déduire que :


- l’itération la plus extérieure (pour explorer le tableau a) est celle qui fournira la
valeur de i,
- l’itération la plus intérieure est donc celle qui comparera les 2 segments.

Nous pouvons ainsi aboutir, pour le programme à générer, aux 2 composants suivants :

Comparer les 2 segments

Fournir la valeur de i

171
Pour la terminaison des itérations, une fonction de terminaison t qui restera > 0 pour
permettre à l’itération la plus extérieure (Fournir la valeur de i) de s’exécuter est :
r = (n – m + 1) – k et il n’existe aucun k pour lequel a[k:k+m] = b[0:m], et
une fonction de terminaison p qui restera > 0 pour permettre à l’itération la plus extérieure
(Comparer les 2 segments) de s’exécuter est : q = m – j.

Une structure affinée de l’algorithme relatif au composant «Fournir la valeur de i» en


tenant compte de toutes les assertions peut être :

début
Q
IA

tant que k ≤ (n – m + 1) et égal = faux faire


rétablir IA
comparer les 2 segments
si les 2 segments sont égaux
alors égal vrai
sinon k k+1

définir i
R
fin

Une structure affinée de l’algorithme relatif au composant «Comparer les 2 segments» peut
alors être :

QB ∧ IB

tant que j ≤ m et tégal = vrai faire


rétablir IB
si a[k+j] ≠ b[j] alors tégal faux
sinon j j+1
RB

On est invité à déterminer :


Q=
IA =
définir i = si égal alors i k sinon i 0
QB =
IB =
rétablir IA =
rétablir IB =
et à démontrer ces 2 algorithmes.

172
50. Soit a[1:n], un tableau d’entiers non vide, dont tous les éléments ont une valeur (n > 1).
Il s’agit de concevoir un programme qui affecte aux variables entières :
- b, le nombre de pentes dans ce tableau,
- m, la longueur maximale des pentes de ce tableau,
- i et j, respectivement indice de début et indice de fin de la pente relative à m.

Un intervalle [i:j] est une pente du tableau a[1:n] ssi :


- 1 ≤ i ≤ j n et
- (a[i] ≤ a[i+1] ≤ … ≤ a[j]) ou (a[i] ≥ a[i+1] ≥ … ≥ a[j]).
Un élément donné peut appartenir à plusieurs pentes.

Ex. : 3, 2, 4, 5

pente de pente de longueur 3


longueur 2

Ex. : 3, 2, 2, 2 contient une seule pente de nature décroissante et de longueur 4,


Ex. : 2, 2, 2, 3 contient une seule pente de nature croissante et de longueur 4,
Ex. : 2, 2, 2, 2 contient une seule pente de nature plate et de longueur 4.

R:

a. Concepts Généraux et Eléments de Théorie

Tous les éléments du tableau a[1:n] doivent être parcourus, il paraît donc évident que
le traitement puisse porter sur chaque élément de ce tableau.
La visite de ces éléments peut se faire de droite vers la gauche ou de gauche vers la
droite et nous retenons ici d’accomplir le parcours de gauche vers la droite.
Il évident que chaque élément appartient à au moins une pente et qu’une pente peut être
croissante, décroissante ou plate.
D’après la théorie, il est donc correct de connaître la nature de la pente courante de
chaque élément du tableau que l’on a à traiter.

Nous aboutissons alors à une architecture logicielle possible suivante :

Traiter les pentes du tableau a[1:n]

b. Raisonnement par récurrence

b1. Considérons que l’on se trouve devant le k ième élément le tableau a[1:n]
(qui est visité de gauche vers la droite), la portion des éléments comprise entre
le 1er élément et le k-1 ème élément étant déjà traitée :

portion déjà traitée


1 k-1 k n

173
Trois cas peuvent arriver :

- si a[k-1] > a[k]


alors : - si la nature np de la pente rencontrée n’est pas encore définie
alors np devient décroissante (d) et sa longueur lp est 2,
- si la nature np est plate (p)
alors np devient décroissante (d) et ajouter 1 à longueur lp,
- si la nature np est décroissante (d)
alors ajouter 1 à longueur lp,
- si la nature np est croissante (c)
alors ajouter 1 à longueur lp, clôturer la pente actuelle,
retenir la pente actuelle et son indice si elle est la plus longue,
np devient décroissante (d),
la longueur lp de la nouvelle pente est 2, et
son indice de début est k-1,

- si a[k-1] < a[k]


alors : - si la nature np de la pente rencontrée n’est pas encore définie
alors np devient croissante (c) et sa longueur lp est 2,
- si la nature np est plate (p)
alors np devient croissante (c) et ajouter 1 à longueur lp,
- si la nature np est décroissante (d)
alors ajouter 1 à longueur lp, clôturer la pente actuelle,
retenir la pente actuelle et son indice si elle est la plus longue,
np devient croissante (c),
la longueur lp de la nouvelle pente est 2, et
son indice de début est k-1,
- si la nature np est croissante (c)
alors ajouter 1 à longueur lp,

- si a[k-1] = a[k]
alors : - si la nature np de la pente rencontrée n’est pas encore définie
alors np devient plate (p) et sa longueur lp est 2,
- si la nature np est plate (p)
alors ajouter 1 à longueur lp,
- si la nature np est décroissante (d)
alors np devient décroissante (d) et ajouter 1 à longueur lp,
- si la nature np est croissante (c)
alors np devient croissante (c) et ajouter 1 à longueur lp.

Ces éléments venant de la théorie des pentes déterminent les concepts de base de
l’invariant et du corps de la boucle principale.

b2. Une transformation simple pour respecter les fondements de l’invariant de la boucle
et pour terminer ce travail est de passer à l’élément suivant du tableau a (k← k+1).

Les points a et b ci-dessus fournissent le cœur de l’algorithme auquel il faudra


annexer des conditions initiales qui établissent l’invariant et des conditions finales
et/ou spécifiques qui ne doivent pas détruire l’invariant.

174
Le développement de ce composant aboutit au cœur suivant :

tant que k ≤ n faire si a[k-1] > a[k]


alors si np = « »
alors np ← «d»
w ← k-1
si np = «p»
alors np ← «d»
lp ← lp+1
si np = «d»
alors lp ← lp+1
si np = «c»
alors z ← k-1
si lp > slp alors slp ← lp
sw ← w
sz ← z
np ← «d»
lp ← 2
w ← k-1

si a[k-1] < a[k]


alors si np = « »
alors np ← «c»
w ← k-1
si np = «p»
alors np ← «c»
lp ← lp+1
si np = «d»
alors z ← k-1
si lp > slp alors slp ← lp
sw ← w
sz ← z
np ← «c»
lp ← 2
w ← k-1
si np = «c» alors lp ← lp+1

si a[k-1] = a[k]
alors si np = « »
alors np ← «p»
w ← k-1
si np = «p» alors lp ← lp+1
si np = «d»
alors np ← «d»
lp ← lp+1
si np = «c»
alors np ← «c»
lp ← lp+1

175
où np = nature de la pente courante,
np = « » indique une nature de pente non encore définie,
np = «c» indique une pente croisssante,
np = «d» indique une pente décroisssante,
np = «p» indique une pente plate,
lp = longueur de la pente courante,
w = indice de début de la pente courante,
z = indice de fin de la pente courante,
slp, sw, sz = respectivement, longueur, indice de début et indice de fin de la pente de
longueur maximale (ou d’une des pentes de longueur maximale) dans
la partie a[1:k-1] du tableau déjà explorée.

Les conditions initiales (qui doivent respecter l’invariant) peuvent être :

lp ← 2
np ← « »
k←2
slp ← 0

et les conditions finales (qui doivent respecter l’invariant) peuvent être :

si lp > slp alors slp ← lp


sw ← w
sz ← z
écrire «la longueur maximale des pentes est :» slp
écrire «ses indice de début et de fin sont : » sw, sz.

L’étudiant doit :
1. formellement écrire et démontrer l’invariant qui a conduit au développement ci-dessus,
2. ajouter le code nécessaire pour fournir le nombre de pentes dans ce tableau.

176
51. Soient 2 tableaux a[1:n], b[1:n] (n≥1) où a[1] ≤ a[2] ≤ …. ≤ a[n],
il est demandé de construire dans b[1:n] une copie de a[1:n] dépourvue de duplications,
c-à-d il s’agit de modifier b[1:n] et m tels que
{a[1], a[2], …, a[n]} = {b[1], b[2], …, b[m]} et b[1] < b[2] < …. < b[m].

52. Construisez un jeu de test «boite noire» en vue de valider toutes ou partie des spécifications
d’un programme qui doit interpréter 3 nombres entiers a, b et c comme représentant les
longueurs des 3 côtés d’un triangle et qui fournit un message adéquat selon le cas.

R : Il s’agit donc de définir des classes d’équivalence Ai et Xi qui couvrent tous les domaines
de données d’interface à l’entrée (c-a-d les domaines de a, de b et de c),
par exemple :
1. classe A1 des valeurs a, b et c pour des triangles rectangles valides,
2. classe A2 des valeurs a, b et c pour des triangles isocèles valides,
3. classe A3 des valeurs a, b et c pour des triangles équilatéraux valides,
4. classe A4 des valeurs a, b et c pour des triangles scalènes valides,
5. classe X1 des valeurs a, b et c tels que une des valeurs est égale à la somme des 2
autres valeurs,
6. classe X2 des valeurs a, b et c tels que une des valeurs est supérieure à la somme des 2
autres valeurs,
7. classe X3 des valeurs a, b et c tels que une des valeurs est égale à 0,
8. classe X4 des valeurs a, b et c tels que 2 de ces valeurs sont égales à 0,
9. classe X4 des valeurs a, b et c tels que les a = b = c = 0,
10. classe X5 des valeurs a, b et c tels que une des valeurs est inférieure à 0.

53. Soient une fonction continue (strictement croissante ou décroissante) sur


l’intervalle [a,b], f[a,b] R, qui possède 1 et 1 seule valeur maximale v, et
une valeur donnée e.
Il est demandé, à l’aide d’un raisonnement par récurrence, de concevoir un
algorithme qui doit trouver une abscisse i telle que |f (i) – v| ≤ e.

R:
a. Concepts Généraux et Eléments de Théorie
Etant donné qu’il est impossible de parcourir toutes les valeurs € [a,b] et
que la fonction f reste strictement croissante ou décroissante sur l’intervalle
[a,b], pour trouver une telle abscisse i dont la valeur f (i) respecte
|f (i) – v| ≤ e, il peut être intéressant de chercher cette abscisse i à l’aide d’un
parcours par sauts sur cet intervalle [a,b].
On ne connaît évidemment pas d’avance l’allure de la fonction f.

177
b. Raisonnement par récurrence
Graphiquement, nous avons la situation suivante :

a x b

b1. Supposons que l’intervalle β, [a,x] ait déjà été exploré et qu’il reste à
investiguer l’intervalle [x,b], il suffit alors de trouver, à l’aide d’un
parcours par sauts, un point w € [x,b] qui guidera la recherche de i :
- si f(w) > f(x) alors le nouvel intervalle de recherche devient [w,b],
- si f(w) < f(x) alors le nouvel intervalle de recherche devient [x,w].

b2. Une Transformation simple est de diviser l’intervalle restant à


investiguer en 3 morceaux égaux, par exemple, en vue de ce parcours par
sauts.

Les points a et b ci-dessus fournissent le cœur de l’algorithme auquel il faudra


annexer des conditions initiales qui établissent l’invariant et des conditions
finales et/ou spécifiques qui ne doivent pas détruire l’invariant.

Nous aboutissons ainsi à l’algorithme suivant :

début
w b-a
tant que |f(b) - f(a)| > e faire p a + (w/3)
u f(p)
q b - (w/3)
v f(q)
si u < v alors a p
sinon b q
w b–a
i (a + b) / 2 (par exemple)
fin

178
54. Soit le module suivant :
début
i 0
z a[0]
tant que j ≤ n faire i i+1
z x * z + a[j]

écrire z
fin

Question 1 : Quelle est l’interface de ce module ?


Question 2 : En utilisant la trace formelle ou toute autre technique de déduction, quelle
est sa spécification ?
Question 3 : Etudiez, analysez et dessinez le flux d’une découpe de ce module.

55. Soient les modules et les spécifications informelles suivants :


EVAL : qui évalue en $ toutes les prestations fournies à un malade donné,
RECH : qui cherche au sein de la base de données A toutes les prestations générées par
chaque service que subit un malade donné entre la date D1 et la date D2,
COLL : qui enregistre au sein de la base de données A chaque prestation relative à un
malade avec tous les paramètres importants relatives à cette prestation.
Chaque service peut générer 1 ou plusieurs prestations.
Ex : Une piqûre (service) donnée à un patient génère une prestation bien codifiée
comprenant : la nature de piqûre, la quantité de liquide injecté, l’identification
du malade, la date, l’heure, … ,
TRAN : qui transforme une prestation en un seul code vital V correspondant.
Un même code vital V peut provenir de plusieurs prestations,
ANAM : qui écrit dans un langage clair (en français), dans la base de données B, et en
fonction du code vital V, toutes les maladies dont souffre un patient,
FACT : qui établit la facture due par un malade,
FACT2 : qui établit une facture (préparée et évaluée).

Il est demandé :
a. de déterminer l’abstraction de chacun de ces modules,
b. d’établir une architecture logicielle composée de ces 6 modules.

179
56. Dans une clinique appelée EPHESE, qui est très fortement fréquentée grâce à
l’expertise de ses médecins, apparaissent des dysfonctionnements fonctionnels qui
génèrent d’autres problèmes de divers types (social, économique,….).

Les dysfonctionnements fonctionnels reposent essentiellement sur les faits suivants :


- difficultés de planifier les consultations car les fiches y relatives ne sont pas
toujours à jour,
- les médecins passent trop de temps sur des tâches administratives (recherche et
classification de fiches, mise à jour des informations de ces fichiers,….), ce qui
allonge inévitablement le temps de service aux malades,
- des traces d’hospitalisation disparaissent,
- gestion inappropriée de suivi de divers services rendus aux malades (services qui
accusent un certain relâchement depuis quelque temps), car ses départements des
gestion (essentiellement la Facturation, les Finances, l’Accueil, la Comptabilité et
le Personnel)
et ses départements techniques (essentiellement l’Entretien, la Pharmacie, la
Cuisine et la Buanderie) sont débordés.

Cette clinique comprend actuellement 300 lits d’hospitalisation partagés entre 12


services médicaux (Maternité, Radiologie, Laboratoire, Kinésithérapie, Pharmacie,
Pédiatrie, Chirurgie, Réanimation, Médecine Interne, Traumatologie, Urologie et
Gynécologie).
Chaque service contient 3 types de chambres :
Chambre à 4 lits, chambre à 2 lits et chambre particulière.
Le nombre de lits dans chaque service varie entre 8 et 24.

Pendant son séjour :


- le patient reçoit un certain nombre de soins, un certain nombre de médicaments et
peut être soumis à un régime alimentaire.
Les prestations, les médicaments et les régimes alimentaires font l’objet d’une
prescription de médecin et sont assurés par les infirmières de service,
- le patient peut, sous autorisation de médecins, changer de service une ou plusieurs
fois et effectuer une sortie temporaire. Il existe 3 espèces de sortie temporaire :
réconfort familial, sortie WE et relâchement.

La fin d’hospitalisation est indiquée par une autorisation de quitter la clinique et le


patient concerné doit libérer le lit et la chambre dans l’heure qui suit.
Tout patient sorti définitivement depuis 1 mois est considéré comme ancien patient et
toutes ses informations seront archivées.
La clinique accepte 3 modes de paiement : paiement totalement supporté par le
malade, paiement totalement supporté par un ou plusieurs tiers (sociétés,
organismes, …) ou paiement supporté par le malade et par un ou plusieurs tiers
(auxquels cas, la clinique produira autant de factures qu’il y a de payeurs).

En début de séjour, chaque patient hospitalisé reçoit automatiquement


une tenue d’intérieur, un peignoir et un pyjama.
La tenue d’intérieur doit être changée tous les 4 jours,
le peignoir, tous les 10 jours et le pyjama tous les 3 jours.
La literie complète est composée de 2 couvertures, de 3 draps de lit, de 2 taies
d’oreiller et d’un protège-matelas.

180
Toute la literie (sauf les couvertures) est à changer tous les 3 jours.
Il faut noter que pour certains patients spécifiquement désignés le changement de
literie et de pyjama se fait quotidiennement.
La buanderie doit procéder à ce changement chaque jour entre 8 h 00 et 9 h 30.

A tout moment, chaque patient ne détient qu’une seule tenue d’intérieur, un seul
peignoir, un seul pyjama et une seule literie complète.

La gestion de ce projet vous est confiée. On vous demande d’aplanir ces


dysfonctionnements fonctionnels en vue d’améliorer et d’innover principalement le
circuit administratif, la gestion des services rendus aux malades et la gestion des
facturations.
En outre, l’architecture mise en place devra permettre de mieux partager
l’information et la communication entre les différents acteurs de EPHESE, de
réduire la consommation de papier, les besoins de déplacements internes et les
délais des procédures administratives.

Cela est l’une des conditions pour que EPHESE entre dans le club prestigieux de
«cliniques d’honneur».

Un des avantages de ce club prestigieux est que EPHESE pourra bénéficier


avantageusement des transferts de certains malades vers d’autres hôpitaux spécialisés
(éparpillés dans le monde entier) affiliés à ce club.

Etant donné les fonctionnalités ci-après, révélées par une première analyse et qui
sont :
1. consulter l’historique des maladies d’un malade,
2. modifier l’adresse d’un tiers garant d’un malade,
3. fournir le nombre total d’un repas donné par service,
4. fournir le nombre total d’un repas donné par jour,
5. fournir une liste ventilée de tous les repas par service,
6. fournir le nombre total de malades qui ont reçu un service donné entre 2 dates
données,
7. mettre à jour l’historique d’un malade,
8. gérer l’admission d’un malade à hospitaliser,
9. fournir le nombre total de malades qui n’ont pas reçu un service donné entre 2
dates données,
10. gérer la fin d’une hospitalisation,
11. mettre à jour des informations sur la médicamentation (interdictions) d’un
malade,
12. gérer le transfert de malades d’un service vers un autre,
13. gérer une demande de consultation,
14. gérer une demande d’examen,
15. facturer les services reçus par un patient.
16. mettre à jour des détails relatifs au mode de paiement d’un malade,
17. consulter les examens en attente pour un malade et les raisons d’attente,
18. gérer le suivi des administrations des médicaments,
19. gérer une sortie temporaire de malade,
20. modifier la liste des ingrédients ou aliments interdits pour 1 malade,

181
21. dresser une liste de patients absents au-delà de la durée légale de leur sortie
temporaire,
22. fournir une liste ventilée de lits et chambres libres au jour et à l’heure,
23. lister les dénominations des tiers garants d’un malade,
24. dresser une liste ventilée (homme et femme) de tenues d’intérieur abîmées,
hors d’usage ou à remplacer,
25. dresser une liste ventilée (homme et femme) de peignoirs abîmés,
hors d’usage ou à remplacer,
26. dresser une liste ventilée (homme et femme) de pyjamas abîmés,
hors d’usage ou à remplacer,
27. dresser une liste de draps abîmés, hors d’usage ou à remplacer,
28. dresser une liste de taies d’oreiller abîmées, hors d’usage ou à remplacer,
29. dresser une liste de protège-matelas abîmés, hors d’usage ou à remplacer,
30. désigner les couvertures à nettoyer ou qui doivent fournir un traitement
spécifique,
31. gérer l’affectation d’une literie (couvertures, draps de lit, taies d’oreiller et
protège-matelas) à un lit donné,
32. gérer l’affectation d’une une tenue d’intérieur, d’un peignoir et d’un pyjama à
un patient donné,

il est demandé de :

1. procéder à une analyse approfondie afin de déterminer une couche de persistance


locale à la fonctionnalité ………… sans trahir aucun élément de ce projet mais en
posant éventuellement toute hypothèse plausible,

2. décomposer plus ou moins sommairement cette fonctionnalité en unités


cohérentes,

3. fournir une structure de chacune des unités,

4. programmer et tester (sommairement) chacune des unités,

5. recomposer plus ou moins sommairement cette fonctionnalité.

182
CHAP. V : ATELIERS ET NOTIONS APPROFONDIES

A. ATELIERS (cfr énoncés ci-avant)

183
B. NOTIONS APPROFONDIES

184

Vous aimerez peut-être aussi