Polymorphisme (informatique)

Un article de Wikipédia, l'encyclopédie libre.
Aller à : navigation, rechercher
Page d'aide sur l'homonymie Pour les articles homonymes, voir Polymorphisme.

En informatique et en théorie des types, le polymorphisme, du grec ancien polús (plusieurs) et morphê (forme), est le concept consistant à fournir une interface unique à des entités pouvant avoir différents types. Par exemple, des opérations telles que la multiplication peuvent ainsi être étendues des scalaires aux vecteurs ou aux matrices, l'addition des scalaires aux fonctions ou aux chaînes de caractères, etc. Il existe plusieurs sortes de polymorphismes fondamentalement différents :

  • le polymorphisme ad hoc, lorsque l'interface unique est implémentée par plusieurs fonctions de même nom, potentiellement hétérogènes, prenant chacune en arguments une combinaison différentes des types des entités. En programmation on parle de surcharge ;
  • le polymorphisme paramétré, lorsque l'interface unique est implémentée par une seule fonction prenant en arguments un type générique pour chaque entité. En programmation orientée objet on parle souvent de programmation générique et en programmation fonctionnelle on parle simplement de polymorphisme ;
  • le polymorphisme par sous-typage (aussi appelé polymorphisme d'inclusion), lorsque l'interface unique est implémentée par plusieurs fonctions de même nom, potentiellement hétérogènes, chacune membre d'une classe dérivée différente de la même hiérarchie de classes formant les types d’une seule entité (un objet). En programmation orientée objet on parle simplement de polymorphisme.

Selon le langage informatique employé, le polymorphisme peut être réalisé par différents moyens, inhérents au langage ou par emploi de patterns.

Polymorphisme ad hoc[modifier | modifier le code]

Polymorphisme paramétré[modifier | modifier le code]

Le polymorphisme paramétré consiste à définir des fonctions qui peuvent être appliquées à des types paramétrés. Par exemple, il est possible de définir une même fonction concat permettant de concaténer deux listes quel que soit le type de données qu'elles contiennent. Dans ce cas, le type de la fonction concat est noté :

\mathrm{concat}: \mathrm{liste}_{\alpha} \times \mathrm{liste}_{\alpha} \rightarrow \mathrm{liste}_{\alpha}

où α est un paramètre qui représente le type de données que contiennent les listes. α peut correspondre aux types entier, réel, chaîne de caractères, listeβ, etc.

Le polymorphisme paramétré est utilisé par les langages Caml, Haskell et Scala. Par exemple, en Haskell, la fonction take renvoie les n premiers éléments d'une liste. La spécification de la fonction renvoyée par l'interpréteur est :

take :: Int -> [a] -> [a]

Le type a est une variable de type qui correspond à n'importe quel type, c'est donc une fonction qui supporte le polymorphisme paramétré.

Polymorphisme par sous-typage[modifier | modifier le code]

Création de types[modifier | modifier le code]

L'idée est de partir d'un type et de le modifier. Par exemple, on peut créer une classe de base, puis faire des classes dérivées.

Ce concept est associé à l'approche orientée objet.

En C++[modifier | modifier le code]

class Forme {
public:
   virtual float Aire() = 0;
};
 
class Carre:public Forme {
public:
   virtual float Aire() { return m_cote*m_cote; }
private:
   float m_cote;
};
 
class Cercle:public Forme {
public:
   virtual float Aire() { return 3.1415926535*m_rayon*m_rayon; }
private:
   float m_rayon;
};

En Java[modifier | modifier le code]

abstract class Forme {
   abstract float aire() ;
}
 
class Carre extends Forme {
   float cote;
   float aire() {
       return cote * cote;
   }
}
 
class Cercle extends Forme {
   float rayon;
   float aire() {
       return Math.PI*rayon*rayon;
   }
}

En C#[modifier | modifier le code]

public abstract class Forme
{
    public abstract float Aire();
}

public class Carre : Forme
{
    private float cote;

    public override float Aire()
    {
        return (float) Math.Pow(cote, 2);
    }
}

public class Cercle : Forme
{
    private float rayon;

    public override float Aire()
    {
        return (float) ( Math.PI * Math.Pow(rayon, 2) );
    }
}

En Eiffel[modifier | modifier le code]

deferred class 
    FORME

feature
    aire: REAL
        deferred end
end


class
    CARRE

inherit 
    FORME

feature
    cote: REAL

    aire: REAL
        do
            Result := cote^2
        end
end


class
    CERCLE

inherit 
    FORME
    MATH

feature
    rayon: REAL

    aire: REAL
        do
            Result := Pi * rayon^2
        end
end

Utilisation des sous-types[modifier | modifier le code]

Grâce aux fonctions virtuelles, on peut faire un algorithme en n'utilisant que la classe de base qui va automatiquement appeler les fonctions des classes dérivées.

En C++[modifier | modifier le code]

float AireTotal(Forme* tabl[], int nb) {
   float s=0;
   for(int i = 0;i<nb; i++) {
      s+= tabl[i]->Aire(); // le programme détermine automatiquement quelle fonction appeler
   }
   return s;
}
 
// ...
Forme* tableau[3] = {new Carre, new Cercle, new Carre};
AireTotal(tableau,3);
// ...

En Java[modifier | modifier le code]

float aireTotal(Forme[] tabl, int nb) {
   float s=0;
   for(int i = 0; i < nb; i++) {
      s += tabl[i].aire(); // le programme détermine automatiquement quelle fonction appeler
   }
   return s;
}

// ...
Forme[] tableau = { new Carre(), new Cercle(), new Carre() };
aireT = aireTotal(tableau, 3);   //aireT aura été défini comme float
// ...

En C#[modifier | modifier le code]

//...
private float _aireT;
readonly Forme[] _tableau = { new Carre(), new Cercle(), new Carre() };
//...

float AireTotal(Forme[] tabl)
{
    float s = 0;

    foreach (Forme form in tabl)
    {
        s += form.Aire(); // le programme détermine automatiquement quelle fonction appeler
    }
    return s;	
}

//...
_aireT = AireTotal(_tableau);
//...

En Eiffel[modifier | modifier le code]

    aireTotal (tab: ARRAY [FORME]): REAL
        do
            -- Result = 0.0 par défaut

            from
                tab.start
            until
                tab.after
            loop
                Result := Result + tab.item.aire
                tab.forth
            end
        end

Intérêt du polymorphisme[modifier | modifier le code]

En proposant d'utiliser un même nom de méthode pour plusieurs types d'objets différents, le polymorphisme permet une programmation beaucoup plus générique. Le développeur n'a pas à savoir, lorsqu'il programme une méthode, le type précis de l'objet sur lequel la méthode va s'appliquer. Il lui suffit de savoir que cet objet implémentera la méthode.

Ainsi, un logiciel de calcul d'intérêt pour des comptes bancaires se présenterait de la façon suivante en programmation classique (pseudo code) :

case MonCompteBancaire
 PEA : MonCompteBancaire.calculeInteretPEA
 PEL : MonCompteBancaire.calculeInteretPEL
 LivretA : MonCompteBancaire.calculeInteretLivretA
end case

Si un nouveau type de compte bancaire PERP apparait (et avec lui un nouveau calcul), il sera nécessaire d'une part d'écrire la nouvelle méthode calculeInteretPERP, mais aussi de modifier tous les appels du calcul ci-dessus. Dans le meilleur des cas, celui-ci sera isolé et mutualisé de sorte qu'une seule modification sera nécessaire. Dans le pire des cas, il peut y avoir des centaines d'appels à modifier.

Avec le polymorphisme, toutes les méthodes porteront le même nom « calculeInteret » mais auront des codes différents (un par type de compte).
L'appel sera de la forme :

MonCompteBancaire.calculeInteret

Lors de l'arrivée du nouveau compte, aucune modification de ce code ne sera nécessaire. Le choix de la méthode réelle à utiliser sera fait automatiquement à l'exécution par le langage, alors que dans le cas précédent, c'est le développeur qui devait programmer ce choix.

Sur les autres projets Wikimedia :