La programmation par contrat

Transcription

La programmation par contrat
La programmation par contrat
Où comment concevoir des
applications qui fonctionnent
du « premier coup »
L’ objet élément essentiel d’un
programme
„
Un objet est présent dans un environnement
{
Il interagit avec d’autres objets
„
„
{
{
„
Communication par message (appel de méthodes)
Consultation/modification de propriétés (accès aux champs)
Il étend les fonctionnalités (services, méthodes,
messages) offerts par des objets
Il encapsule des données ou des concepts qui sont
manipulés par des processus
Problème :
{
Comment garantir qu’un objet s’intègre bien dans son
environnement ?
La fiabilité d’une application
„
Deux notions clés relativement à la fiabilité
{
{
„
La correction : permet de garantir que l’utilisation d’un
objet dans l’application ne pourra pas engendrer d’erreurs
La robustesse : permet de garantir que si un objet (ou
une opération effectuée par un objet) est défaillante
l’application récupérera de l’erreur
Opposition entre les deux approches
{
{
La correction : détection avant d’exécuter une opération
illicite (compilation/exécution)
La robustesse : traitement s’effectuant après qu’une
opération illicite se soit produite
Le contrat : un moyen de
garantir la correction
Idée :
{
{
Un contrat formel est défini entre le processus utilisateur
et l’objet utilisé
Si l’objet utilisé (ou client) par le processus respecte
toutes les clauses imposées par processus utilisateur,
aucune erreur ne pourra se produire
Principe :
{
{
{
Le processus utilisateur émet un ensemble de demandes
(claims)
L’objet utilisé définit un ensemble de propriétés
(responsibilities)
L’adéquation des propriétés de l’objet utilisé avec les
demandes du processus forment le contrat
Mise en œuvre d’un contrat
Conception d’un objet/module
„
On souhaite qu’un processus donné utilise cet objet
{
„
Adhésion de l’objet aux prérequis du processus
Les processus définis dans cet objet/module seront utilisés d’autres objets
{
{
Les objets ne sont pas définis : définition des prérequis nécessaires pour l’utilisation
des objets
Les objets sont déjà définis : adaptation des processus pour se satisfaire des
propriétés des objets
Idée
Chaque programmeur doit respecter les prérequis et contraintes des autres
programmeurs
L’adhésion de tous les objets aux contrats garantit la correction
Spécifier un contrat
„
Les différents éléments pouvant être mis dans un
contrat
{
{
{
{
{
{
La liste des méthodes qu’une classe doit fournir et leur
actions
Les valeurs acceptables pour les champs et propriétés
d’un objet
Les conditions sur les arguments acceptables pour une
méthode
Les conditions sur les valeurs retournées par les
méthodes d’un objet
Les conditions sur le temps d’exécution d’une méthode
d’un objet
Les invariants propres à l’objet
Précondition/Postcondition
Les triplets de Hoare
{ P } A {Q}
où
{
{
{
P : définit un ensemble de conditions sur les variables et
champs qui doivent être vérifiées avant que A soit exécuté
Q : définit un ensemble de conditions sur les variables et
champs qui doivent être vérifiées après que A fut exécuté
A : l’expression à exécuter
Deux cas de figure
Correction totale : { P } A {Q} si P est vrai A termine
et vérifie Q.
Correction partielle : { P } A {Q} si P est vrai et A
termine alors A vérifie Q.
Conditions fortes/faibles
(Weak and strong conditions)
„
Une condition peut-être plus ou moins
fortes
{
{
Précondition forte : favorable au
concepteur de l’objet utilisé /
défavorables à l’utilisateur
Postcondition forte : favorable à
l’utilisateur / défavorable au concepteur
de l’objet utilisé
Comment s’assurer qu’un objet
respecte bien un contrat ?
Premier point du contrat à vérifier:
Définir les méthodes et champs qu’une classe doit
fournir
Deux approches possibles :
Définir une classe de base purement abstraite à
surcharger (approche C++)
Ajouter au langage une construction permettant de
définir les méthodes et champs qu’une classe doit
fournir (approche Java)
S’assurer de l’implantation des
méthodes
„
Nous considérons le compteur
{
{
{
„
En C++
{
„
Doit fournir une méthode de remise à zéro (reset)
Doit fournir une méthode pour passer à l’état suivant (next)
Doit fournir deux méthodes pour accèder en lecture et écriture à
la valeur du compteur (getCounter, setCounter)
Définir une classe CounterBase purement abstraite qui expose
ses méthodes virtuelles
En Java
{
Définir une interface ICounter qui expose les méthodes devant
être implantées
S’assurer de l’implantation des
méthodes
En C++
En Java
class CounterBase
{
…
public:
public interface ICounterBase
{
…
public void next();
public void reset();
public int getCounter();
public int setCounter();
…
}
virtual void next() = 0;
virtual void reset() = 0;
virtual int getCounter()
const = 0;
virtual int
setCounter(int) = 0;
…
}
S’assurer de l’implantation des
méthodes
En C++
En Java
class MyCounter:
public CounterBase
{
…
public:
public class MyCounter
implements ICounterBase
{
…
public void next()
virtual void next()
{…}
virtual void reset()
{…}
virtual int getCounter()
const {…}
virtual int
setCounter(int)
{…}
…
}
{…}
public void reset()
{…}
public int getCounter()
{…}
public int setCounter()
{…}
…
}
S’assurer de l’implantation des
méthodes
„
Comment s’effectue le contrôle ?
{
{
A la compilation
En C++
„
{
En Java
„
„
Refuse de compiler une classe si la classe n’implémente pas toutes
les méthodes définies dans la classe « interface »
Le contrôle vérifie :
{
{
„
Refuse de créer une classe si la classe possède des méthodes
virtuelles purement abstraites
Que la méthode est bien exposée
Que les types des arguments et du résultat sont les bons
Le contrôle ne vérifie pas:
{
{
Que l’exécution de la méthode est correcte
Que les valeurs passées en paramètre ou retournées sont les
bonnes
Comment s’assurer qu’un objet
respecte bien un contrat ?
Deuxième point du contrat à vérifier:
Vérifier que les arguments passés aux méthodes
d’un objet sont correctes
Ceci correspond à vérifier que la condition {P} de
l’expression {P} A {Q} est vérifiée
La vérification doit avoir lieu avant d’exécuter A
Méthode : ajouter dans le code avant d’exécuter A une
assertion.
Comment implanter une assertion
testant une precondition
En C++
En Java
void f(int* a, int i)
{
if(a != NULL || i == 0)
throw Exception(
"Assertion failed");
…
}
public void f(int[] a, int i)
{
if (!(a != null && i >= 0
&& i < a.length))
throw new
IllegalArgumentError(
"Assertion failed");
…
}
void f(int* a, int i)
{
assert(a != NULL || i ==
0)
…
}
Fonctionnement de l’assertion
Le code correspondant à la condition est exécuté avant
que le code de A est exécuté
Si la condition n’est pas vérifiée, une exception est
lancée
Problème :
{
L’erreur est détectée uniquement lors de l’exécution
„
{
Implique une phase de test précise du programme
Par défaut le programme s’arrête !
„
Implique de rendre le programme robuste, ie. de récupérer
et de traiter l’exception (cf. cours sur les exceptions)
Comment s’assurer qu’un objet
respecte bien un contrat ?
Troisième point du contrat à vérifier:
Vérifier que les valeurs retournées par un méthode sont
correctes si l’exécution termine (correction partielle)
Ceci correspond à vérifier que la condition {Q} de l’expression
{P} A {Q} est vérifiée si l’exécution de A termine.
La vérification doit avoir lieu après avoir fini d’exécuter A
Méthode : ajouter dans le code après l’exécution de A une
assertion.
Si la condition n’est pas vérifiée, une exception est lancée
Comment implanter une assertion
testant une postcondition
En C++
En Java
void f(int* a, int i)
{
assert(a != NULL || i ==
0);
…
assert(result != 0);
return result;
}
public void f(int[] a, int i)
{
int result;
if (!(a != null && i >= 0
&& i < a.length))
throw new
IllegalArgumentError(
"Assertion failed");
…
if (result != 0)
throw new
IllegalArgumentError(
"Assertion failed");
}
Ce qu’il reste à tester ?
„
Les invariants propres à l’objet
{
„
Il est possible d’ajouter de vérifier au début de
chaque méthode et à la fin de chaque méthode
que l’invariant est respecté
Les conditions sur le temps d’exécution
d’une méthode d’un objet
{
{
Faire confiance au programmeur !
Utiliser des outils de preuve automatique (cf.
Module électif)