• Aucun résultat trouvé

2.4.1 Conception par contrat

La notion de contrat a été définie pour exprimer les droits et les devoirs des classes dans le paradigme orienté objet. L’expérience a montré que l’expression des contraintes de fonctionnement d’un logiciel sous forme de contrats non ambiguës est une approche de conception valide [Jézéquel'97]. Bertrand Meyer a nommé cette approche la conception par contrats (Design by Contract) [Meyer'92a]. La conception par contrats impose aux concepteurs de séparer les responsabilités du client et celles de l’implantation d’une méthode. Ces contrats sont définis par des expressions booléennes nommées pré-condition et post-condition d’une méthode, et invariant d’une classe. Le client d’une classe appelle les méthodes en respectant les pré-conditions et les invariants de cette classe. Dans ce cas, la méthode assure qu’après son exécution, ses post-conditions et les invariants de sa classes sont respectés. Ces contrats sont vérifiés dynamiquement pendant l’exécution du système. Ils signalent une erreur quand ils sont violés.

Plusieurs langages de programmation supportent la conception par contrat : nativement dans Eiffel, avec des extensions comme Contract4J pour Java, par exemple.

Dans l’ingénierie des modèles, le standard pour l’expression de contrats est OCL (Object Constraint Language [OMG'97b]. Il a été développé en même temps qu’UML pour contraindre les différents diagrammes UML. En particulier il permet de définir des invariants de classes, des pré et post-conditions de méthodes. Une contrainte OCL est une expression booléenne sans effet de bord qui est fortement typée et écrite dans un contexte. Un contexte peut être soit une classe, soit une opération. Les trois contraintes suivantes présentent un exemple d’utilisation basique d’OCL dans un modèle de programme bancaire :

context CompteBancaire::créditer(montant : Integer)

pre : montant > 0 //avant l’opération de crédit le montant doit être positif post : solde = solde@pre + montant //après l’opération, le nouveau solde

//doit être = à l’ancien + le montant

context CompteBancaire

inv : self.solde >= self.autorisationDécouvert

//à tout instant le solde d’une instance de CompteBancaire ne doit //pas être inférieur à l’autorisation de découvert

39 Contraintes et composants

Le formalisme introduit par OCL est préférable au langage naturel car il supprime les ambiguïtés et peut être exploité automatiquement dans les programmes. L’introduction du MOF et d’UML 2.0 ont conduit à l’adaptation du méta-modèle d’OCL [OMG'03] pour permettre d’associer des contraintes aux éléments des différents méta-modèles conformes au MOF.

2.4.2 Exploitation des contraintes pour le test

Les contrats obtenus par conception par contrats sont utiles pour le test de programmes. Ils représentent une version exécutable de la spécification et permettent de relever la présence d’erreurs quand ils sont violés à l’exécution. Leitner et Ciupa ont réalisé différents travaux dans l’équipe de Meyer pour exploiter les contrats comme oracle du test [Meyer'07, Leitner'07, Ciupa'08, Ciupa'05]. Leur approche de test exploite la présence des contrats créés pendant le développement d’un système suivant la conception par contrat. Cette approche permet de s’affranchir de la construction des oracles pendant la phase de test et de considérer uniquement l’automatisation de la génération de données de test. L’inconvénient des contrats issus de la conception par contrat réside dans le fait qu’ils sont écrits pendant le développement, par le développeur. Si celui-ci commet des fautes il y a des risques qu’il les fasse à la fois dans le programme et dans les contrats. Il n’y a pas la séparation nécessaire entre le développement et le test.

L’exploitation des contrats permet néanmoins de récupérer à moindre coût des oracles à condition de s’assurer de leur correction par rapport à la spécification. Cette utilisation des contrats a été retenue dans différents travaux étudiant l’oracle et considérant les contrats au sens plus large comme des contraintes [Le Traon'06, Briand'03]. Elles peuvent aussi bien être des assertions inclus dans un programme de test, que des contrats d’un programme sous test, embarqués ou non dans le code du programme.

Les langages permettant la définition d’assertions pour la vérification d’états du programme existent depuis longtemps : Anna [Luckman'85] pour Ada, APP (Annotation Pre-Processor) [Rosenblum'95] pour C, Eiffel [Meyer'92b]. D’autres systèmes qui intègrent le support d’assertions pour le test unitaire ont été développés plus récemment : JUnit pour Java [Cheon'02], SPARK pour ADA [Barnes'03], Spec# pour C# [Barnett'05].

Il existe également des travaux sur l’utilisation d’OCL pour la vérification statique de propriétés sur des modèles. Un exemple est pOCL [Millan'08] qui propose une extension du langage OCL pour permettre de mettre en relation plusieurs modèles dans une même contrainte par la définition de contexte multiple.

2.4.3 Composant dans l’IDM

La notion de composant MDA est apparue comme une entité essentielle pour le succès du déploiement du MDA. Dans [Bézivin'03d], ce type de composant est défini comme “a packaging unit for any artifact used or produced within an MDA-related process”. Bézivin et al. introduisent plusieurs concepts et entités présents dans un contexte MDA et fournissent plusieurs exemples de composants MDA. S’ils présentent les transformations de modèles comme le plus important composant, ils considèrent également les méta-modèles, les profils

UML. Bendraou et al. [Bendraou'08] proposent aussi de considérer les composants MDA pour factoriser les artéfacts du développement dirigé par les modèles. Fondement et al. [Fondement'04] proposent d’utiliser des composants IDM (MDE component) pour répondre à des besoins méthodologiques. Ils analysent quels sont les différentes technologies disponibles pour améliorer la réutilisation et définir des entités qui permettent d’automatiser un développement dirigé par les modèles. Ils considèrent les dépendances entre packages, les profils, les transformations, les méta-modèles, et ils montrent que ces mécanismes permettent l’assemblage de composants mais ont des limites quant à l’adaptation de ces composants. Comme dans les travaux de Bézivin, ce travail considère une large définition des composants IDM. Nous parlerons dans le chapitre 5 uniquement de composants de transformations de modèles.

Nous traiterons ce dernier point en exploitant une méthodologie définie dans l’équipe Triskell pour la définition de composants autotestables [Jézéquel'01]. Cette méthodologie a déjà été étudiée dans le paradigme orienté objet [Baudry'03]. Nous l’adapterons à l’ingénierie des modèles et l’utiliserons pour l’expérimentation de nos travaux dans le chapitre 5. Cette solution consiste à embarquer dans un composant son implantation, ses tests, et sa spécification. Les composants ont la possibilité d’exécuter leurs tests. Pour être autotestable, le composant embarque également sa spécification sous forme exécutable avec des contrats. Le Traon et al. [Le Traon'06] définissent qu’un composant est vigilant s’il embarque des contrats suffisamment précis pour détecter les états erronés du composant pendant l’exécution. Il est possible alors de dynamiquement contrôler que le composant accepte les modèles qu’on lui présente et produit des modèles corrects. En particulier, les contrats assurent de l’appartenance des modèles manipulés au domaine d’entrée et au domaine de sortie de la transformation, ce qui permet d’assembler plusieurs composants.