UPMC Paris Universitas Master Informatique STL Cours Composant 2. Qualité logicielle et spécications algébriques c 2005-2008 Frédéric Peschanski UPMC Paris Universitas 24 février 2008 c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 1 / 39
Plan du cours 1 Qualité logicielle Facteurs externes Facteurs internes 2 Méthodologie Approche classique Approche orientée composant 3 Spécications algébriques Introduction Principes de base Complétude et cohérence Dépendances explicites c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 2 / 39
Bibliographie Remarque Ce cours est inspiré du travail et des publications de Betrand Meyer (créateur de la conception par contrat et du langage Eiel). Références Livre : Bertrand Meyer, Conception et programmation objet. 2005, Eyrolles Site : http://www.eiffel.com c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 3 / 39
Notion de qualité logicielle Notion de qualité logicielle peu étudiée en tant que telle malgré son importance. Dicile à aborder objectivement Nous présentons (rapidement) l'étude intéressante de B. Meyer Préliminaires : séparation des rôles Rôle : qui juge de la qualité? Fournisseur du logiciel? Facteurs internes, point de vue plutôt technique Rôle de développeur, point de vue micro Rôle d'architecte, point de vue macro Client (du fournisseur)? Facteurs externes, point de vue moins technique Rôle d'utilisateur Rôle d'acquéreur Question : quels sont les rôles les plus importants? c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 4 / 39
Critères d'évaluation Pour chaque facteur de qualité Quelle est sa dénition? Quelle est son importance relative? Comment l'évaluer? du point de vue du fournisseur (évaluation en amont) du point de vue du client (évaluation en aval) Comment l'améliorer/l'optimiser? c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 5 / 39
Facteurs externes Du point de vue de l'acquéreur Correction remplir correctement sa tâche, telle que dénie par sa spécication Approches formelles : logiciel sûr Approches formelles légères : conception par contrat, génération automatique de tests, etc. Approches empiriques : test, test, et test Robustesse réagir de façon satisfaisante à des conditions anormales de lenvironnement, prévues (spéciées explicitement) ou non (adaptabilité) Extensibilité facilité de mise-à-jour du logiciel pour intégrer des changements dans les spécications etc. c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 6 / 39
Facteurs externes (suite) Du point de vue de l'acquéreur Compatibilité s'intégrer de façon satisfaisante dans l'environnement d'exploitation du client Ecacité utiliser un minimum de ressources de fonctionnement Economie remplir sa tâche au coût le plus bas possible Ponctualité logiciel délivré au client dans les délais prévus, ou avant etc. c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 7 / 39
Facteurs internes Du point de vue du fournisseur Simplicité, Modularité, Généricité Découplage, indépendance de la représentation etc. c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 8 / 39
Composants et qualité Dans ce cours, nous nous intéressons en particulier aux trusted components, des composants en lesquels on puisse avoir conance Le critère de qualité primordial est la correction. Prérequis spécications précises, claires et vériables (donc formelles) spécications (co-)algébriques vériabilité des implémentations vis-à-vis des spécications conception par contrats (vérif. dynamique) et logique de Hooare (vérif. statique) c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications24 algébriques février 2008 9 / 39
Cycles de développement classiques Cycle en cascade (dépassé) Cycle en V (courant) Analyse des besoins Spécications Conception architecturale Conception détaillée Recette Tests de validation Tests d'intégration Tests unitaires Cycles semi-itératifs (Y,λ,Rup) Cycles de maintenance Implémentation février 2008 10 / 39
Cycles de développement agiles Cycles itératifs (RAD) extreme Programming (XP) Program rst (think later) Test-driven Pair programming (code & review) Refactoring (conception au l de l'eau) Client involved etc. autres Cycles agiles (AM,AUP,etc.) février 2008 11 / 39
Cycle de développement orienté composant Component-based Software Engineering (CBSE) Architecture logicielle : identication en amont des composants logiciels Componentisation Croiser les spécications avec l'existant (emphase sur la réutilisabilité) bibliothèques de composants Développement de nouveaux composants approches descendantes (en V ou semi-itératif) approches ascendantes (agile, XP) Phase de composition ou assemblage (intégration) février 2008 12 / 39
Cycle de développement : trusted components Dance cours : approche globalement ascendante, localement descendante Modélisation globale par composition (au niveau spécications) Modélisation locale par ranement formel : 1 Spécications algébriques des services (interfaces de composants) 2 Conception par contrat des implémentations 3 Code critique : vérications formelles en logique de Hoare Packaging et intégration via OSGi février 2008 13 / 39
Spécications formelles Besoins Langage de spécication permettant de décrire ce que doit faire un composant logiciel : de façon précise et non-ambigüe ( formalisation) indépendemment des implémentations de façon cohérente et vériable sans surspécier (notion de complétude) Approche formelle légère utilisation de spécications (co-)algébriques, lointain héritier des types de données abstraits (ADT) + relativement simples d'utilisation, base des contrats - expressivité relativement limitée février 2008 14 / 39
Principes Fondations Ensembles et fonctions du premier ordre Logique typée du premier ordre Notion d'observation (vision co-algébrique) Spécication Brique élémentaire : le Service devant être proposé par le fournisseur à ses clients indépendant des implémentations plusieurs implémentations du même service une meme implémentation peut requérir/fournir plusieurs services Cahier des charges minimal (et formel) pour les implémentations février 2008 15 / 39
Format des spécications Service : nom du service spécié Types : dépendances des types élémentaires Require : dépendances de services Observators : fonctions d'observation de l'état... Constructors : fonctions de construction... Operators : fonctions de modication... Observations : Axiomes d'observation... Types élémentaires : boolean, int, double, String, etc. février 2008 16 / 39
Exemple : les cuves quantity Tank Pipe c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications 24algébriques février 2008 17 / 39
Service cuve Questions Quel est le nom du service? Que veut-on observer sur chaque fournisseur du service? février 2008 18 / 39
Service cuve Questions Quel est le nom du service? Que veut-on observer sur chaque fournisseur du service? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean février 2008 18 / 39
Service cuve Questions Quel est le nom du service? Que veut-on observer sur chaque fournisseur du service? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Signature des observateurs nomobs : [Service] T 1... T n T février 2008 18 / 39
Service cuve Questions Quel est le nom du service? Que veut-on observer sur chaque fournisseur du service? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Signature des observateurs nomobs : [Service] T 1... T n T Remarque : [Service] signie ce Service (this) février 2008 18 / 39
Constructeurs Question Comment construire une Cuve? février 2008 19 / 39
Constructeurs Question Comment construire une Cuve? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Constructors : init : [Tank] février 2008 19 / 39
Constructeurs Question Comment construire une Cuve? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Constructors : init : [Tank] Signature des constructeurs init : T 1... T n [Service] février 2008 19 / 39
Operateurs Question Comment manipuler une Cuve? février 2008 20 / 39
Operateurs Question Comment manipuler une Cuve? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Constructors : init : [Tank] Operators : ll : [Tank] double [Tank] pump : [Tank] double [Tank] février 2008 20 / 39
Operateurs Question Comment manipuler une Cuve? Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Constructors : init : [Tank] Operators : ll : [Tank] double [Tank] pump : [Tank] double [Tank] Signature des opérateurs nomop : [Service] T 1... T n [Service] février 2008 20 / 39
Fonctions partielles Questions Quels sont les domaines de dénition des observateurs, constructeurs et opératurs? Quelle est l'approche de spécication? favoriser le client : approche permissive favoriser le fournisseur : approche défensive février 2008 21 / 39
Fonctions partielles Questions Exemple Quels sont les domaines de dénition des observateurs, constructeurs et opératurs? Quelle est l'approche de spécication? favoriser le client : approche permissive favoriser le fournisseur : approche défensive approche défensive Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Constructors : init : [Tank] Operators : ll : [Tank] double [Tank] precondition : ll(b,q) require q 0 pump : [Tank] double [Tank] precondition : pump(b,q) require q 0 getquantity(b) q 0 février 2008 21 / 39
Fonctions partielles Questions Exemple Quels sont les domaines de dénition des observateurs, constructeurs et opératurs? Quelle est l'approche de spécication? favoriser le client : approche permissive favoriser le fournisseur : approche défensive approche permissive Service : Tank Types : boolean,double Observators : getquantity : [Tank] double isempty : [Tank] boolean Constructors : init : [Tank] Operators : ll : [Tank] double [Tank] precondition : ll(b,q) require q 0 pump : [Tank] double [Tank] precondition : pump(b,q) require q 0 février 2008 22 / 39
Résumé : fonctions Signature des fonctions nomfunct : T 1... T n T precondition nomfunct(x 1,..., x n) require P où P est une formule logique du premier ordre avec x 1,..., x n des variables quantiées universellement sur leur domaine T 1,..., T n Remarque par défault, P true février 2008 23 / 39
Observations : dénitions Dénitions préliminaires Expression bien formée composition de fonctions respectant les types de domaines de dénitions (ex. : pump(ll(b, q 1 ), q 2 )) Expression d'observation expression bien formée dont la fonction de niveau principal est un observateur (ex. : getquantity(pump(ll(b, q 1 ), q 2 ))) Formule bien formée expression bien formée à valeur booléenne (ex. : getquantity(pump(ll(b, q 1 ), q 2 )) > 0) Observation égalité de type O = E où O est une expression d'observation de type T et E une expression à valeur dans T (ex. : getquantity(pump(ll(b, q 1 ), q 2 )) = getquantity(b) + q 1 q 2 )) février 2008 24 / 39
Observations : objectifs Objectifs Spécier, en tant qu'axiomes, les observations minimales permettant de caractériser de façon cohérente (et optionnellement complète) la sémantique du service spécié. Important les observations sont des expressions axiomatiques, on ne doit pas pouvoir les déduire les unes des autres. Approche méthodologique 1 Essayer de minimiser les observateurs invariants 2 Observer, avec les observateurs non dérivables, les diérentes possibilités de construction 3 Croiser chaque observateur non dérivable avec chaque opérateur février 2008 25 / 39
Phase 1 : minimisation des observateurs Objectif principal Essayer de minimiser les observateurs Objectif secondaire Relier les observations entre elle lorsque cela fait sens (invariants utiles). invariants février 2008 26 / 39
Phase 1 : minimisation des observateurs Objectif principal Essayer de minimiser les observateurs Objectif secondaire Relier les observations entre elle lorsque cela fait sens (invariants utiles). invariants Exemple retour aux cuves Service : Tank etc. Observations : [invariants] // catégorie des observations isempty(b) = ( getquantity(b) = 0 ) // minimisation getquantity(b) 0 // invariant février 2008 26 / 39
Phase 1 : minimisation des observateurs Objectif principal Essayer de minimiser les observateurs Objectif secondaire Relier les observations entre elle lorsque cela fait sens (invariants utiles). invariants Exemple retour aux cuves Service : Tank etc. Observations : [invariants] // catégorie des observations isempty(b) = ( getquantity(b) = 0 ) // minimisation getquantity(b) 0 // invariant Remarque inutile d'observer isempty dans la suite février 2008 26 / 39
Phase 2 : observer les constructeurs Objectif décrire les observations minimales sur les constructeurs février 2008 27 / 39
Phase 2 : observer les constructeurs Objectif décrire les observations minimales sur les constructeurs Service : Tank etc. Observations : [invariants] isempty(b) = ( getquantity(b) = 0 ) getquantity(b) 0 [init] getquantity(init(b)) = 0 février 2008 27 / 39
Phase 2 : observer les constructeurs Objectif décrire les observations minimales sur les constructeurs Service : Tank etc. Observations : [invariants] isempty(b) = ( getquantity(b) = 0 ) getquantity(b) 0 [init] getquantity(init(b)) = 0 Remarque on suppose que chaque expression respecte les domaines de dénitions (sinon on ne peut pas écrire l'expression correspondante) février 2008 27 / 39
Phase 3 : observer les opérateurs Objectif décrire les observations minimales sur les opérateurs février 2008 28 / 39
Phase 3 : observer les opérateurs Objectif décrire les observations minimales sur les opérateurs Service : Tank etc. Observations : [invariants] isempty(b) = ( getquantity(b) = 0 ) getquantity(b) 0 [init] getquantity(init(b)) = 0 [ll] getquantity(ll(b,q)) = getquantity(b) + q [pump] getquantity(pump(b,q)) = getquantity(b) q février 2008 28 / 39
Phase 3 : observer les opérateurs Objectif décrire les observations minimales sur les opérateurs Service : Tank etc. Observations : [invariants] isempty(b) = ( getquantity(b) = 0 ) getquantity(b) 0 [init] getquantity(init(b)) = 0 [ll] getquantity(ll(b,q)) = getquantity(b) + q [pump] getquantity(pump(b,q)) = getquantity(b) q Remarque approche défensive (préconditions plus fortes, observations/postconditions plus faibles) février 2008 28 / 39
Phase 3 : observer les opérateurs Objectif décrire les observations minimales sur les opérateurs Service : Tank etc. Observations : [invariants] isempty(b) = ( getquantity(b) = 0 ) getquantity(b) 0 [init] getquantity(init(b)) = 0 [ll] getquantity(ll(b,q)) = getquantity(b) + q [pump] getquantity(pump(b,q)) = max(0,getquantity(b) q) Remarque approche permissive (préconditions plus faibles, observations/postconditions plus fortes) février 2008 29 / 39
Cohérence et complétude Analyse d'un service Cohérence On peut déduire au plus une valeur de chaque observation Complétude On peut déduire au moins une valeur de chaque observation Objectifs La cohérence est primordiale. Toute incohérence est un bug de spécication. La complétude est importante mais est parfois dicile, voir impossible, à obtenir. En pratique, on acceptera donc si c'est justié de sous-spécier. En revanche, on essaiera dans la mesure du possible de ne pas sur-spécier (élimination des redondances). Remarque en CPS on donnera des arguments essentiellement informels concernant la cohérence et la complétude. février 2008 30 / 39
Interactions Les spécications de service permettent de décrire l'interface interne des composants (fournisseurs/implémenteurs du service). Il nous manque la description de l'interface externe ou contexte d'utilisation du service. Prérequis certains services ont besoins (require) d'autres services pour être spéciés. Exemple le tuyau (Pipe) reliant deux cuves. Dans ce cas, on ajoute la section Require aux spécications et on observe explicitement les services requis. Service : nom du service etc. Require : services requis etc. février 2008 31 / 39
Exemple : service tuyau Service : Pipe Require : Tank Types : boolean, double, null Observators : getquantity : [Pipe] double const getcapacity : [Pipe] double const getintank : [Pipe] Tank const getouttank : [Pipe] Tank isopenin : [Pipe] boolean isopenout : [Pipe] boolean Constructors : init : Tank Tank double [Pipe] precondition init(i,o,c) require I null O null c > 0 Operators : switchin : [Pipe] [Pipe] precondition switchin(p) require isopenout(p) switchout : [Pipe] [Pipe] precondition switchout(p) require isopenin(p) ush : [Pipe] [Pipe] precondition ush(p) require isopenin(p) isopenout(p) c 2005-2008 Frédéric Peschanski (UPMCCours ParisComposant2. Universitas) Qualité logicielle et spécications 24algébriques février 2008 32 / 39
Constantes Un observateur constant est décoré par le mot clé const Pour un observateur C constant, il est uniquement nécessaire de décrire les observations de C sur les constructeurs. Pour un service s et un opérateur op qui n'est pas un constructeur, on aura implicitement : C(op(s,...)) = C(s,...) février 2008 33 / 39
Constantes Un observateur constant est décoré par le mot clé const Pour un observateur C constant, il est uniquement nécessaire de décrire les observations de C sur les constructeurs. Pour un service s et un opérateur op qui n'est pas un constructeur, on aura implicitement : C(op(s,...)) = C(s,...) Par exemple, si on retire le const de l'observateur getcapacity de capacité de tuyau : [init] getcapacity(init(i,o,c)) = c [switchin] février 2008 33 / 39
Observations Service : Pipe etc. Observations : [invariants] getquantity(p) 0 getcapacity(p) isopenin(p) xor isopenout(p) [init] getquantity(init(i,o,c)) = 0 getcapacity(init(i,o,c)) = c getintank(init(i,o,c)) = I getouttank(init(i,o,c)) = O isopenin(init(i,o,c)) = false isopenout(init(i,o,c)) = false [switchin] getquantity(switchin(p)) =? etc.? février 2008 34 / 39
Observations Service : Pipe etc. Observations : [invariants] getquantity(p) 0 getcapacity(p) isopenin(p) xor isopenout(p) [init] getquantity(init(i,o,c)) = 0 getcapacity(init(i,o,c)) = c getintank(init(i,o,c)) = I getouttank(init(i,o,c)) = O isopenin(init(i,o,c)) = false isopenout(init(i,o,c)) = false [switchin] getquantity(switchin(p)) =? etc.? Comment exprimer les interactions entre les cuves et le tuyau? février 2008 34 / 39
Observations composées Important observation des interactions entre services On sépare clairement : la cause qui est l'opérateur (ou le constructeur) du service courant que l'on souhaite spécier La conséquence interne ou modication (changement d'état observable) du service spécié La conséquence externe qui correspond à la modication d'un service externe Par exemple : cause ouverture de la porte d'entrée du tuyau (opérateur switchin) conséquence interne remplissage du tuyau conséquence externe la cuve d'entrée se vide au moins partiellement février 2008 35 / 39
Observation externe On peut spécier une observation externe de plusieurs façon : utiliser un observateur obs du service externe S par S.obs ex. : Tank.getQuantity(... ) utiliser un opérateur ou constructeur op du service externe par S.op. Dans ce cas, cela sous-entend que l'on eectue exactement la conjonction des observations spéciées pour l'opérateur op. ex. : Tank.ll(T,q) correspond à Tank.getQuantity(Tank.ll(T,q))= Tank.getQuantity(T)+q février 2008 36 / 39
Exemple : observation externe directe cause ouverture de la porte d'entrée du tuyau (opérateur switchin) conséquence interne remplissage du tuyau d'un volume v conséquence externe la cuve d'entrée se vide du volume v On pose avail(p) def = getcapacity(p) getquantity(p) la capacité de remplissage actuelle du tuyau. On pose également v def = min(tank.getcapacity(getintank(p)), avail(p)) le volume maximum déversable de la cuve vers le tuyau.... [switchin] getquantity(switchin(p)) = getquantity(p) + v Tank.getQuantity(getInTank(switchIn(P))) = Tank.getQuantity(getInTank(P)) - v Tank.getQuantity(getOutTank(switchIn(P))) = Tank.getQuantity(getOutTank(P)) Important : en plus d'observer ce qui change (ex. InTank) il est nécessaire d'observer ce qui ne change pas (ex. OutTank). février 2008 37 / 39
Exemple : opérateur externe cause ouverture de la porte d'entrée du tuyau (opérateur switchin) conséquence interne remplissage du tuyau d'un volume v conséquence externe la cuve d'entrée se vide du volume v On pose avail(p) def = getcapacity(p) getquantity(p) la capacité de remplissage actuelle du tuyau. On pose également v def = min(tank.getcapacity(getintank(p)), avail(p)) le volume maximum déversable de la cuve vers le tuyau.... [switchin] getquantity(switchin(p)) = getquantity(p) + v getintank(switchin(p))) = Tank.pump(getInTank(P),v) getouttank(switchin(p)) = getouttank(p) février 2008 38 / 39
Fin Fin février 2008 39 / 39