Un morceau de programme
est re-utilisable si on peut l'employer facilement dans
plusieurs contextes. Par exemple, un module est re-utilisé
par tous les programmes qui l'importent.
Dans les
langages fortément typés, la vérification
de contraintes de type présentes dans le
contexte d'utilisation
assurent la cohérence finale du programme.
Par exemple, un programme qui importe un module, pose comment
contrainte que celui-ci ait une certaine interface.
L'utilisation du module n'est
possible qu'après vérification de cette contrainte.
Dans la plupart des langages procéduraux classiques, la re-utilisation impose une concordance exacte entre le type éffectif du composant re-utilisé et le type attendu dans le contexte où il doit être employé. Dans les langages objet cette concordance peut être approximative. Rappelons, dans l'exemple suivant, que la classe compteremunere est une sous-classe de compte.
let c = new compte 100;; let cr = new compteremunere 200;; let est_crediteur (x:#compte) = x#donne_solde >= 0;; val est_crediteur : #compte -> bool = <fun>
La notation (x:#compte)
est lue
x:Type_Instance_de(compte)
Considérons le code suivant:
est_crediteur(c);; est_crediteur(cr);;La fonction est_crediteur attend un objet instance de compte (par exemple, c). On peut donc voir l'appel est_crediteur(cr) comment une tentative de re-utiliser la fonction sûr un objet cr dont le type éffectif n'est pas instance de compte. Dans un langage classique, cet appel provoque une erreur de typage car les types Type_Instance_de(compte) et Type_Instance_de(compteremunere) sont considérés incompatibles. Dans les langages objet, cet appel est valide grâce à la règle suivante, dite de polymorphime de l'approche objet5. Nous considérons deux classes quelconques c et cs.
Polymorphisme dans l'approche objet:
Soit cs une sous-classe de c,
La première version exprime la règle en termes des objets vus comme des entités générés à partir de leurs classes. La deuxième, exprime la règle du point de vue des types des objets. Informellement, un objet d'une certaine classe peut être employé là où une autre classe d'objet est attendue, si le premier possède au moins tous les attributs du deuxième. C'est le cas de tout objet appartenant à une sous-classe de la classe attendue. Les attributs additionnels dans le premier, restent alors invisibles par le code initialement prevu pour la classe moins étendue: ils sont préservés mais ne sont pas accessibles. Ainsi, selon cette régle, le type d'un objet est polymorphe: il s'agit du type donné par sa classe, mais aussi par tous les types de ses super-classes.
est_crediteur c;; - : bool = true est_crediteur cr;; - : bool = true
En résumé, l'héritage, et avec la lui, la notion de sous-classe
entraîne une utilisation polymorphe des constructions objet.
Le but recherché est celui d'augmenter les chances de re-utiliser
du code, tout en donnant la possibilité d'enrichir incrémentalement
celui-ci.
Mais dans les langages objets, toute re-utilisation n'est pas l'unique fruit du polymorphisme, ou ce qui est équivalent, de l'héritage. La combinaison de deux autres mécanimes favorise également la re-utilisation. Ce sont la re-définition et la liaison tardive. Ainsi, au chapitre des fonctionnalités fondamentales des langages objet, et qui font sans doute leur succès, on compte: