Index Suivant

Notion de barrière d'abstraction

 
 
C'est une notion que vous connaissez déjà!
 
En effet, pour utiliser une fonction, nous avons dit qu'il fallait regarder sa spécification -- et non sa définition --, spécification qui est une description, une « abstraction » de ce que rend la fonction.
 
;;; fn: Donnee -> Resultat 
;;; (fn d) rend ... 
 

 
(define (fn d) ...)
 

 
Ainsi, on peut considérer qu'il y a une barrière entre la fin de la spécification et sa définition proprement dite et, lors d'une utilisation, on n'a pas besoin (et il faudrait même se l'interdire) de franchir cette barrière.
 
Nous avons vu aussi que, bien entendu, du côté de celui qui écrit la définition de la fonction -- on dit qu'il l'implante --, il faut que cette implantation réponde à la spécification.
 
Barrière d'abstraction  
Souvent, on ne peut pas implanter une fonction toute seule sans tenir compte de l'implantation d'autres fonctions, pour avoir un ensemble cohérent.
 
Aussi, regroupe-t-on toutes ces fonctions dans ce qu'on appelle une barrière d'abstraction. Comme pour les fonctions, une barrière d'abstraction a un aspect abstrait (c'est l'ensemble des spécifications des fonctions) et un aspect concret (c'est l'implantation des fonctions).
 
Prenons un exemple (uniquement didactique): nous voudrions manipuler l'identité des individus, identité qui est composé d'un nom et d'un prénom. Les fonctions utiles sont alors identite (qui crée une identité à partir d'un nom et d'un prénom), prenom (qui rend le prénom d'une identité donnée) et nom (qui rend le nom d'une identité donnée).
 
 

 
Ensuite, lorsque l'on veut définir une fonction dont une donnée ou le résultat est un élément du domaine de la barrière d'abstraction, on n'utilise que les fonctions de la barrière d'abstraction (on s'interdit d'utiliser la connaissance que l'on pourrait avoir de l'implantation de cette barrière d'abstraction):
 
 

 
Mais, bien entendu, il faut aussi implanter cette abstraction.
 
 

 
L'intérêt (fondamental en génie logiciel) est que l'on peut ensuite changer facilement l'implantation de la barrière d'abstraction (en général pour avoir une implantation plus performante): il suffit de modifier cette implantation et, comme les fonctions utilisatrices de la barrière d'abstraction ne sont définies qu'avec les fonctions de cette barrière, sans utiliser la connaissance des détails de l'implantation, elles restent parfaitement valides.
 
Exemple de différentes implantations pour une même barrrière d'abstraction  
Prenons l'exemple de l'identité des individus. La spécification de la barrière d'abstraction est:  
;;; identite : string * string -> Identite 
;;; (identite nom prenom) rend l'identité dont le nom est «nom» et le  
;;; prénom est «prenom» 
 
;;; nom : Identite -> string 
;;; (nom id) rend le nom de l'identité «id» 
 
;;; prenom : Identite -> string 
;;; (prenom id) rend le prénom de l'identité «id» 
 

 
Écrivons une fonction qui, étant donnée une identité, rend la chaîne de caractères composée du prénom de l'individu, suivi d'un espace et terminée par le nom de l'individu:  
;;; ident->string : Identite -> string 
;;; (ident->string id) rend la chaîne de caractères composée du prénom de l'individu «id»,  
;;; suivi d'un espace et terminée par le nom de l'individu 
(define  (ident->string id)
  (string-append (prenom id) " " (nom id)))
 

 
Il ne reste plus qu'à implanter la barrière d'abstraction. Vous pensez peut-être qu'il n'y a qu'une solution: un n-uplet qui comporte deux chaînes de caractères. Mais, tout de suite, on voit qu'il y en a deux selon que l'on mette le nom avant ou après le prénom. Ces deux solutions étant très proches, on ne voit pas pourquoi on changerait (mais, lorsque l'on écrit des fonctions sur cette structure de données, il faut savoir dans quel cas on est: il est alors beaucoup plus parlant d'utiliser les fonctions nom et prenom que les fonctions car et cadr). Voici cette implantation:
 
;;; identite : string * string -> Identite 
;;; (identite nom prenom) rend l'identité dont le nom est «nom» et le  
;;; prénom est «prenom» 
(define (identite nom prenom) 
  (list nom prenom))
 
;;; nom : Identite -> string 
;;; (nom id) rend le nom de l'identité «id» 
(define (nom id) 
  (car id))
 
;;; prenom : Identite -> string 
;;; (prenom id) rend le prénom de l'identité «id» 
(define (prenom id) 
  (cadr id))
 

 
Mais on pourrait aussi utiliser le type Paragraphe, en mémorisant le prénom dans la première ligne et le nom dans la seconde ligne:
 
;;; identite : string * string -> Identite 
;;; (identite nom prenom) rend l'identité dont le nom est «nom» et le  
;;; prénom est «prenom» 
(define (identite nom prenom) 
  (paragraphe (list prenom nom)))
 
;;; nom : Identite -> string 
;;; (nom id) rend le nom de l'identité «id» 
(define (nom id) 
  (cadr (lignes id)))
 
;;; prenom : Identite -> string 
;;; (prenom id) rend le prénom de l'identité «id» 
(define (prenom id) 
  (car (lignes id)))
 

 
Et il y en a (au moins) une quatrième! L'identité des individus intervient dans tous les fichiers qui comportent des informations sur des personnes, par exemple le fichier de la scolarité qui contient votre cursus, vos notes... Or, dans ce fichier, les identités sont mémorisées sous forme d'une chaîne de caractères obtenue en concaténant le nom, puis une virgule et enfin le prénom. Ainsi, n'est-il pas aberrant d'avoir une autre implantation de cette barrière d'abstraction (nous ne donnons pas les définitions des fonctions string-avant-virgule et string-apres-virgule car elles sont hors programme):
 
;;; identite : string * string -> Identite 
;;; (identite nom prenom) rend l'identité dont le nom est «nom» et le prénom est «prenom» 
(define (identite nom prenom) 
  (string-append nom "," prenom))
 
;;; nom : Identite -> string 
;;; (nom id) rend le nom de l'identité «id» 
(define (nom id) 
  (string-avant-virgule id))
 
;;; prenom : Identite -> string 
;;; (prenom id) rend le prénom de l'identité «id» 
(define (prenom id) 
  (string-apres-virgule id))
 
avec
 
;;; string-avant-virgule : string -> string 
;;; (string-avant-virgule s) rend le préfixe de «s» avant la première occurrence du caractère virgule 
;;; HYPOTHÈSE: il y a une occurrrence du caractère virgule dans «s» 
 
;;; string-apres-virgule : string -> string 
;;; (string-apres-virgule s) rend le suffixe de «s» après la première occurrence du caractère virgule 
;;; HYPOTHÈSE: il y a une occurrrence du caractère virgule dans «s» 
 

 
Mise en oeuvre en DEUG MIAS  
Par la suite, nous voudrons souvent avoir plusieurs implantations d'une même barrière d'abstraction et tester des fonctions utilisatrices de cette barrière d'abstraction avec chaque implantation de la barrière. Afin de minimiser les « copier--coller », nous utiliserons une architecture logicielle particulière que nous décrivons ci-dessous sur notre exemple.
 
Ainsi, les définitions Scheme des différentes fonctions de l'exemple ci-dessus sont écrites dans deux fichiers:  
La définition de cette dernière fonction utilisant la barrière d'abstraction, nous demandons que le fichier identite1.scm soit inclus en début de fichier grâce à la fonction inclure qui a comme spécification:
 
;;; inclure : string -> 
;;; (inclure s) inclue le fichier de nom «s». Tout se passe comme si le texte 
;;; du fichier de nom «s» était écrit à la place de cette application. 
 

 
Ainsi, le fichier identToString.scm commence par:
 
;;;; Utilise identite 
(inclure "identite1.scm")
 

 
On peut schématiser cette architecture par:
 
 

 
Si l'on veut une autre implantation de la barrière d'abstraction, il suffit  
 
 

 
Remarque: dans la pratique,  
Ainsi, notre fichier identToString.scm (qui permet de tester trois versions de la barrière d'abstraction) est:  
;;;; Id: identToString.scm,v 1.1 2002/09/20 15:18:07 titou Exp  
;;;; Utilise identite : 
; (define (version-identite) "identite1.scm")  
(define (version-identite) "identite2.scm")
; (define (version-identite) "identite3.scm") 
(inclure (version-identite))
 
;;; ident->string : Identite -> string 
;;; (ident->string id) rend la chaîne de caractères composée du prénom de l'individu «id»,  
;;; suivi d'un espace et terminée par le nom de l'individu 
(define  (ident->string id)
  (string-append (prenom id) " " (nom id)))
 
;;; essai de ident->string : 
(display  (string-append "Essai avec " (version-identite)))
(verifier ident->string 
  (ident->string (identite "Curie" "Pierre")) == "Pierre Curie")
 

Pour s'auto-évaluer
  • Exercices d'assouplissement

  • Auteur(s): titou@ufr-info-p6.jussieu.fr.Mainteneur de la page: titou@ufr-info-p6.jussieu.fr.

    Index Suivant