« Méthodes de génie logiciel avec Ada/Cinquième partie » : différence entre les versions

Contenu supprimé Contenu ajouté
Aucun résumé des modifications
Aucun résumé des modifications
Ligne 6 :
C'est ce processus que nous allons tenter d'illustrer dans cette partie. Le but n'est donc pas tant de développer «une méthode de plus» que de montrer comment les idées que nous avons développées peuvent être mises en œuvre à l'intérieur d'un cadre méthodologique fondé sur les méthodes existantes.
=Cahier des charges de la méthode=
==EnoncéÉnoncé==
Comme indiqué dans le titre de cette partie, nous voulons développer une méthode pour des projets de taille moyenne (soit, en première approximation, de 10000 à 100000 lignes de code). Nous visons donc une classe de problèmes qui requiert une véritable équipe de développement, mais de taille telle qu'il soit encore possible d'organiser des réunions rassemblant la totalité de l'équipe. Il s'agit d'une méthode de conception détaillée: on suppose que l'on dispose d'un cahier des charges (qui n'est pas forcément ni aussi complet, ni aussi précis que l'on pourrait le souhaiter) et que le but est d'obtenir un programme réalisant ce cahier des charges. En particulier, le problème des spécifications matérielles est supposé résolu, ou tout au moins suffisamment avancé pour permettre aux développeurs logiciels de ne pas s'en soucier.
 
Ligne 144 :
<references />
 
<li>EcrireÉcrire le corps des opérations de l'objet à concevoir</li>
Les composants étant spécifiés, il faut les assembler pour construire l'implémentation de l'objet d'origine. Concrètement, ceci signifie que nous pouvons écrire le corps en principe définitif de l'objet à concevoir avec toutes ses opérations. Ce corps sera compilé; un problème à ce niveau signifie généralement que les objets utilisés ne sont pas correctement définis. On ne s'étonnera donc pas de trouver ici encore quelques oscillations entre la définition des objets utilisés et l'implémentation des opérations de l'objet à concevoir. Noter que les corps sont des enchaînements d'actions, effectuées par les opérations des objets utilisés. Une analyse en programmation structurée est donc tout à fait appropriée pour décrire cette phase.
 
On essayera d'éviter d'avoir à documenter ces implémentations; en effet, il s'agit d'une partie purement algorithmique et nous avons déjà dit combien nous préférions nous appuyer sur un code autodocumentéauto-documenté. Bien que cela représente un idéal parfois impossible (et souvent difficile) à atteindre, la simple lecture du code ne doit laisser que peu de doutes au lecteur qui aurait lu et compris la documentation de spécification des composants utilisés, ce qui est le minimum que l'on soit en droit d'exiger de lui.
 
AÀ l'issue de cette étape, on dispose d'un plan d'abstraction complètement compilé.
 
<li>Contester</li>
Ligne 158 :
* Réutilisabilité
Deuxième épreuve: imaginer comment réagit la conception à des changements des exigences. Essayer d'imaginer de nouveaux services que pourrait demander le «client» (c'est-à-dire celui qui nous impose les spécifications de l'objet à définir). Les objets définis nous permettraient-ils de répondre à la demande?
** Idéalement, il suffirait d'assembler les mêmes composants différemment, preuve que notre conception est à la fois générale et puissante. EventuellementÉventuellement, il faudrait rajouter de nouveaux composants, qui ne perturberaient pas ceux que nous avons définis: on peut alors dire que le système est peut-être incomplet, mais extensible.
** Moins bon: il faut généraliser les composants existants en leur rajoutant des fonctionnalités nouvelles; ne pourrait-on le faire tout de suite? Il est souvent plus simple de prévoir largement les services fournis au moment de la conception initiale que d'avoir à les augmenter ensuite.
** Mauvais: il faut modifier le composant; une évolution risque alors de perturber non seulement le composant, mais également ses utilisateurs. On a alors un phénomène de diffusion de l'action d'évolution dans des parties du projet qui n'auraient pas dû être concernées. Il est impératif de remettre en cause la solution choisie.
* Élémentarité
*Elémentarité
Troisième épreuve: l'objet est-il élémentaire? Chaque objet doit être une abstraction complète et insécable: il prend en charge TOUT un aspect d'UNE SEULE entité. Il faut donc vérifier qu'il n'est pas possible de le séparer en deux objets distincts. En bonne logique, il faudrait aussi vérifier que deux modules séparés ne peuvent pas être réunis en un seul objet; en pratique, ce test est moins nécessaire, car lorsque ce problème surgit, il aboutit souvent à des impossibilités de compilation, diagnostiquées lors de l'étape précédente.
 
On doit également se poser les questions suivantes: l'objet est-il simple? Correspond-il naturellement à un objet du monde réel ? AÀ la limite, on devrait pouvoir prendre n'importe quel non-informaticien qui passe dans le couloir et lui expliquer ce qu'est l'objet.
 
<li>Caractérisation</li>
Ligne 217 :
Le langage Ada est utilisé comme moyen d'expression des décisions de conception à toutes les phases du projet. Ceci peut surprendre en donnant l'impression de «coder» tout de suite. Le but est d'avoir un langage d'expression unique depuis la conception jusqu'à la réalisation; cela n'est possible que grâce au très haut niveau d'abstraction fourni par Ada. L'utilisation de la méthode avec un langage de plus bas niveau serait difficile, voire dangereuse, car il ne serait pas possible d'exprimer des décisions de conception sans faire en même temps des décisions d'implémentation – ce que l'on veut éviter.
 
La méthode essaie de limiter la quantité de documentation, notamment en fournissant autant que possible du code autodocumentéauto-documenté. Ceci n'est possible que parce que la méthode ne vise que les projets de taille moyenne; cette politique peut ne pas être applicable à des projets de taille plus importante. Enfin, la méthode ne formalise pas les contraintes temporelles au-delà de la possibilité de gérer un «budget temps». Elle n'est donc pas adaptée au développement de projets temps réel critiques.
 
En résumé, son domaine d'efficacité maximum est celui de la réalisation d'applications informatiques courantes, sans contraintes de certifications ni de performances trop exigeantes et de taille moyenne... ce qui représente tout de même une bonne part des développements logiciels.
Ligne 246 :
<ol style="list-style-type:lower-alpha">
<li>Identifier les objets utilisés</li>
AÀ partir de la définition informelle précédente, nous allons essayer d'identifier les principaux objets utilisés.
 
Le problème comporte à l'évidence une notion centrale qui est la boisson. Il convient cependant d'éviter de se précipiter vers une définition trop hâtive; comme cet élément est très important, de nombreuses vues sont possibles et nous ne maîtrisons pas encore assez bien le problème pour la définir. Retenons simplement pour l'instant qu'il existe «quelque chose» appelé boisson. Nous devrons gérer de l'argent, qui est manifestement une grandeur d'un type numérique approprié. Le consommateur est évidemment un objet important du monde réel. Toutefois, il est totalement extérieur au système. Le consommateur n'est pas géré par le logiciel et il n'interagit pas non plus directement avec le logiciel. Ce n'est donc pas un objet intéressant pour notre vue actuelle.Le monnayeur se chargera de tout ce qui est lié à la gestion de l'argent: décompte du montant introduit et rendu de monnaie.
Ligne 354 :
 
<li>Contester</li>
Maintenant que nous avons une première structure sur laquelle appuyer notre pensée, il faut regarder de plus près et inspecter si nous n'avons pas omis des détails, manqué de précision dans les définitions, ou oublié une fonctionnalité. En particulier, nous n'avons pas envisagé les cas exceptionnels: épuisement des produits, non-disponibilité de monnaie, annulation de la demande par l'utilisateur. De plus, le programme principal effectue une attente active, ce qui est toujours gênant. Nous avons eu la prudence d'introduire un delaydélai pour éviter des blocages sur un système monoprocesseur, mais une solution sans attente active serait préférable.
* Cas du produit épuisé
Nous avons dit que le menu doit toujours renvoyer un choix valide, c'est-à-dire qu'il ne doit pas prendre en compte un produit épuisé. Au premier niveau, il est commode de le spécifier ainsi, mais il faut quand même se poser la question de la faisabilité de cette spécification. Autrement dit: comment le menu peut-il savoir quels produits peuvent être fabriqués? Ou bien le menu peut interroger l'unité de fabrication, ou bien il faut que «quelqu'un» prévienne le menu qu'un produit n'est plus disponible. La première solution introduit un couplage supplémentaire entre des unités qui n'ont aucune raison de se connaître, aussi vaudrait-il mieux l'éviter. La seconde implique de déterminer qui doit prévenir le menu. Ce ne peut être que l'unité de fabrication (mais on introduit de nouveau un couplage avec le menu) ou le programme principal. Comme celui-ci connaît de toute façon le menu et l'unité de fabrication, on n'introduirait pas de couplage supplémentaire.
Ligne 443 :
Nous ne voyons pas a priori d'autres cas d'exception; mais si un problème imprévu se produisait, il faut en tout état de cause rendre son argent à l'utilisateur. Il convient donc de prévoir un traitement d'exception «rattrape-tout».
* Le problème de l'attente active
On appelle «attentes actives» les boucles qui ne font qu'attendre qu'un événement se produise, comme dans le cas de la boucle d'attente sur le montant introduit. Une telle boucle est toujours gênante, car elle accapare l'unité centrale sans rien faire d'utile. Il faut toujours mettre un delaydélai dans ce cas, autrement cette boucle pourrait empêcher d'autres parties du programme de s'exécuter... y compris celles chargées de changer la condition de boucle. Mais la meilleure solution consiste à les éviter. Pour cela, il faut se poser la question suivante: quel est le besoin de plus haut niveau qui nous a conduit à cette boucle? En l'occurrence, c'est d'attendre qu'un certain montant soit introduit. Attendre le prix d'un produit pourrait parfaitement être un service rendu par le monnayeur, faisant ainsi disparaître l'attente active, au moins à ce niveau d'abstraction. L'implémentation la fera peut-être réapparaître, à moins que nous ne trouvions une autre solution. Au moins, en reportant ce problème vers les couches plus profondes, ouvrons-nous la possibilité d'autres solutions. La spécification finale du monnayeur devient donc:
<syntaxhighlight lang="ada">
with Définition_Argent; use Définition_Argent;
Ligne 606 :
n'est pas atomique; si l'utilisateur introduit une pièce entre le moment où la valeur de la monnaie à rendre est calculée et le moment où la procédure est appelée, il y a un risque de rendre un montant incorrect. C'est pourquoi le Montant_Introduit est calculé après avoir bloqué le monnayeur; si l'utilisateur continue d'introduire des pièces, elles retomberont directement dans la sébile sans être encaissées et ne gêneront donc pas le programme. Pensons également qu'une pièce peut être introduite après que nous sommes revenu de la procédure Attendre; ce n'est pas gênant, car elle sera comptabilisée normalement et nous en tiendrons donc compte au niveau de Rendre_Monnaie.
 
Une autre condition de course risque de se produire au niveau de l'instruction select: que se passe-t-il si l'utilisateur appuie sur le bouton d'annulation juste après que nous avons mis la variable Annulé à False ? Si nous sommes sorti de l'instruction, nous ne nous en apercevrons même pas: l'utilisateur a pressé le bouton trop tard. Si nous ne sommes pas encore sorti du select, la partie avant then abort sera exécutée, y compris la remise à True de la variable Annulé. La fourniture du produit sera ou bien totalement annulée, ou pas du tout, mais aucun cas intermédiaire ne peut se produire.
 
Bien entendu, nous devons immédiatement ajuster le corps maquette de Monnayeur pour refléter ces modifications (on le trouvera en annexe), puis recompiler et vérifier la nouvelle maquette. De cette façon, nous nous assurons qu'à tout moment le programme reste stable et vérifié.