dans Programmation

La programmation par contrat en pratique

Suite à l’article précédent sur TDD, abordons la programmation par contrat, terme inventé par le créateur du langage Eiffel Bertrand Meyer. Je sais bien qu’il existe de nombreux articles et publications sur le sujet. Je vais donc synthétiser tout ce que j’ai pu lire dans différents ouvrages (papiers ou en ligne) et illustrer le concept avec le projet des Pages blanches introduit dans le post sur TDD.

La programmation par contrat, qu’est ce que c’est ?

Une façon de programmer où l’on érige des règles à respecter par différentes parties qui signent, en quelque sorte, un contrat. Si les parties prenantes respectent ce dernier, alors on garantit le bon fonctionnement de l’objet du contrat.

En termes moins théoriques, l’utilisateur d’un morceau de code s’engage à respecter certaines conditions pour l’utiliser ; le code appelé s’engage lui aussi à fournir le résultat attendu. Les deux sont liés : si l’appelant respecte les conditions de l’appelé, il est en droit d’attendre des résultats corrects ; de même, si l’appelé voit ses conditions respecté, il doit tout faire pour répondre aux attentes de l’appelant.

Les conditions que l’appelant doit respecter sont appelées les pré-conditions. Celles que l’appelé doit garantir à la fin du traitement sont appelées les post-conditions. Celles qui doivent être garanties tout le long de l’exécution sont les invariants.

Illustrons avec un carré (exemple tiré de l’excellent livre Coder efficacement – Bonnes pratiques et erreurs à éviter (en C++) de Philippe Dunski). Une pré-condition à la création d’un carré serait que la longueur des côtés ne soit pas négative ou nulle. C’est au créateur du carré de vérifier ça. Une post-condition est que, pour une longueur valide, l’aire du carré est bien égale au carré de la longueur. Si ce n’est pas le cas, c’est l’implémentation, l’appelé, qui est en défaut. Enfin, un invariant serait qu’il y ait bien quatre côtés de même longueur. S’il y en a plus ou moins, ou s’ils ne sont pas tous exactement de la même longueur, ce n’est pas un carré.

Les avantages de la PpC

  • Une documentation vivante et à jour du code. On sait, d’un rapide coup d’œil, les valeurs valides ou non que peut prendre la fonction, ce qu’elle retourne comme résultat ou comme erreur en cas de non-respect du contrat, les effets de bords éventuels en cas de rupture, etc. Et avec des langages comme D qui offre un support natif, ou Java qui permettent d’ajouter ces informations en générant le code, on gagne en puissance.

Exemples tirés respectivement de la page Wikipédia anglaise des invariants de classe et d’un article sur iContract.

  • On responsabilise les parties. Je sais que l’utilisateur est bête. Mais à trop vouloir sécuriser ses entrées, on le déresponsabilise et il se dit que, peu importe ce qu’il passe, de toute façon l’appelé fera le nécessaire pour tester. Et bien non ! S’il ne respecte pas mes pré-conditions, pourquoi devrais-je respecter les post-conditions et tenter de rattraper ses erreurs ? C’est de sa faute.
  • On améliore la qualité. Le but d’un contrat est de garantir le bon fonctionnement d’un morceau de code si toutes les parties prenantes respectent leurs engagements. Ainsi, si un bug ou un comportement inhabituel est découvert, il suffit de regarder où le contrat à été rompu. Je pense, en écrivant cette phrase, à un système de logs qui notifierait là où rupture est apparue.

Cas pratique

Si on regarde le constructeur d’une page, on constate que celui-ci ne prend qu’un argument qui est l’URL à charger. Or, comme le programme est spécifique aux pages blanches, il ne sert à rien d’essayer de vouloir charger une page autre. D’où les deux pré-conditions suivantes.

Si l’utilisateur ne respecte pas ces conditions, alors je n’ai aucune raison de respecter le contrat moi non plus. De même, dans le cas du découpage d’une ligne, j’attends plusieurs choses, notamment que la ligne ne soit ni nulle ni vide et qu’elle contienne le bon nombre de champs. En retour, j’offre la garantie qu’il y a bien le bon nombre d’informations dans la Line que je renvoie.

Des cas exceptionnels

Il ne faut pas tomber dans l’excès de mettre des assertions dans tous les sens. Certaines erreurs ne sont la faute ni de l’appelant, ni de l’appelé. Une connexion impossible à établir, une allocation qui échoue par manque de mémoire, un fichier manquant, tous ces cas sont exceptionnels. Là, mieux vaut privilégier l’utilisation des exceptions et leur possible rattrapage plutôt qu’une assertion qui claque bien sèchement.

Que faire en cas de rupture ?

Oubliez la personne concernée. Si le contrat est rompu, plus rien n’est garanti. Si le programme n’est pas critique (sous-entendu si des vies ne sont pas en jeu), alors mieux vaut corriger celui-ci en analysant le code fautif. Si le contrat a été rompu, c’est qu’il y avait un problème dans des données, donc inutile de continuer à travailler avec celles-ci.

Je suis partisan, dans le cas où une post-condition n’est pas remplie, d’envoyer une exception afin de le signaler précisément à l’appelant. Pourquoi ? Il semblerait qu’on le préconise, comme ici ou ici. L’idéal est d’envoyer une exception de runtime.

Pour quelques liens de plus

J’ai essayé de regrouper le plus possible ce que je sais tout en étant clair. Mais comme la force d’internet c’est d’offrir plusieurs sources et plusieurs points de vue, voici des liens collectés au fil du temps.

  • Les pages Wikipédia en français et en anglais sur la PpC.
  • Une liste de trois articles par lmghs sur la théorie, les assertions et vue détaillée de ce qu’on peut faire en matière de PpC avec C++.
  • Coder efficacement – Bonnes pratiques et erreurs à éviter (en C++) de Philippe Dunski.
  • Conception et Programmation Orientées Objet de Bertrand Meyer.

Laisser un commentaire