Introduction
Il est important de comprendre que la théorie présentée ici n’est pas une notion de programmation orientée objet, mais bien une notion propre à certains langages comme Java et C#.
Qu’est-ce qu’une interface en Java/C#
En Java et C#, on appelle interface une liste de définition de méthodes qu’une classe doit fournir à ses clients (méthode publique). D’une manière similaire, une interface est équivalente à une classe abstraite ne contenant que des méthodes abstraites (aucun attribut ni méthode effective ou implémentée).
Lorsqu’une classe « hérite » d’une interface, on dit que cette classe implémente une interface. On utilise ce terme pour s’assurer de ne pas confondre l’arbre d’héritage et la structure d’interface.
À quoi sert une interface en Java/C#
On voit que l’interface ne peut pas être utilisée afin d’encapsuler du code (méthodes) ou des données (attribut), comme peuvent le faire des classes. On peut donc se demander à quoi peut servir une interface.
L’avantage d’une interface par rapport à une classe est que (en Java et C#) est qu’il est impossible d’hériter de plus qu’une seule classe (ce qu’on appelle héritage simple). Par contre, malgré certaines problématiques causées par l’héritage multiple (que nous étudierons dans le cours de programmation orientée objet 2), ce type d’héritage a plusieurs avantages majeurs (diminution de la duplication de code, augmentation de la réutilisation de code, facilité de maintenance, polymorphisme, etc.) L’interface permet aux langages Java et C# d’avoir l’avantage du polymorphisme de l’héritage multiple, sans avoir à gérer les inconvénients de l’héritage multiple.
Exemple
L’utilisation de l’héritage multiple est variée, mais je peux tout de même vous montrer un exemple où ça pourrait s’appliquer.
Supposons que, dans notre système, nous avons la structure d’héritage suivante:
De plus, dans le même système, nous avons la structure suivante:
Supposons que nous voulons ajouter un nouvel Animal qui peut également être utilisé comme véhicule. Quelque chose comme ceci:
Nous voyons que ce « design » est impossible en Java et C# puisque ces langages ne gèrent pas l’héritage multiple et que le Cheval hérite de deux classes différentes. Pour régler cette problématique, nous pouvons utiliser un « design » avec interface. Par exemple:
Voici le code de l’interface « Pilotable » et « Cheval ». Au niveau où vous êtes rendu, vous devriez être capable de programmer vous-même les autres classes.
L’interface Pilotable:
/**
* Interface commune aux objets qui peuvent être pilotés.
*
* @author Louis Marchand (prog@tioui.com)
* @version 0.1, lundi avr 05, 2021 17:13:42 EDT
*
* Distribué sous licence MIT.
*/
public interface Pilotable {
/**
* Embarquement dans l'objet Pilotable.
*/
public void embarquer();
/**
* Arrête d'avancer.
*
* @see #avancer
*/
public void arreter();
/**
* L'objet pilotable commence à avancer.
*
* @see #arreter
*/
public void avancer();
}
La classe Cheval:
/**
* Classe représentant un Animal de race chevaline.
*
* @author Louis Marchand (prog@tioui.com)
* @version 0.1, lundi avr 05, 2021 17:09:37 EDT
*
* Distribué sous licence MIT.
*/
public class Cheval extends Animal implements Pilotable{
/**
* Le son qu'émet l'Animal
*/
public void cri() {
System.out.println("Hiiiiii");
}
/**
* Arrête d'avancer
*
* @see #avancer
**/
public void arreter() {
System.out.println("Arrête de marcher.");
}
/**
* Embarquement sur le Cheval
*/
public void embarquer() {
System.out.println("Embarquement sur le cheval.");
}
/**
* Constructeur du Cheval
*
* @param aNom Valeur de nom
* @param aAge Valeur de age
*/
public Cheval(String aNom, int aAge) {
super(aNom, aAge);
}
}
Voici maintenant un exemple de polymorphisme qui utilise cet interface:
public void demarrerTransport(Pilotable aPilotable) {
aPilotable.embarquer();
aPilotable.avancer();
}
On pourrait utiliser cette méthode comme ceci:
Cheval lCheval = new Cheval("Blacky", 10);
Automobile lAutomobile = new Automobile();
demarrerTransport(lCheval);
demarrerTransport(lAutomobile);
Particularité de Java
Depuis les dernières versions de Java, il est possible de mettre du code de méthode à l’intérieur d’une interface. De cette manière, nous pouvons aller chercher certains autres avantages de l’héritage multiple, comme l’encapsulation de code.
Malheureusement, puisque le langage Java n’a pas été créé avec l’héritage multiple en tête, le code pouvant être placé dans une interface est assez limité. Des exemples de limitations sont:
-
- Impossibilité de placer des attributs dans l’interface;
- Aucune gestion de conflit (si deux interfaces possèdent chacune une méthode avec la même signature);
- etc.
À propos de Eiffel
Puisque Eiffel est un langage de programmation orienté objet permettant l’héritage multiple, il n’est pas nécessaire de recourir aux interfaces. Il suffit d’hériter directement des classes que nous désirons. Nous pouvons donc programmer sans problème la structure suivante en Eiffel.
Voici cet exemple dans le langage Eiffel.
Classe ANIMAL:
note
description: "Un animal dans le programme d'animalerie."
auteur: "Louis Marchand"
date: "Mercredi, 25 Août 2021 19:29:24 +0000"
revision: "0.1"
deferred class
ANIMAL
feature {NONE} -- Initialisation
make(a_nom:STRING; a_age:INTEGER)
-- Initialisation de `Current' assignant `a_nom' à `nom'
-- et `a_age' à `age'
do
set_nom(a_nom)
set_age(a_age)
end
feature -- Accès
nom:STRING
-- L'identificateur texte de `Current'
age:INTEGER
-- Le nombre d'année depuis la naisance de `Current'
set_nom(a_nom:STRING)
-- Assigne `a_nom' à `nom'
do
nom := a_nom
end
set_age(a_age:INTEGER)
-- Assigne `a_age' à `age'
do
age := a_age
end
cri
-- Le bruit que fait `Current'
deferred
end
avancer
-- `Current' se déplace
do
print("L'animal se met à avancer.%N")
end
end
Classe VEHICULE:
note
description: "Représente un véhicule dans le programme l'animalerie."
auteur: "Louis Marchand"
date: "Mercredi, 25 Août 2021 19:29:24 +0000"
revision: "0.1"
deferred class
VEHICULE
feature -- Accès
embarquer
-- `Current' reçoit un usager
do
print("L'usager embarque dans le véhicule.%N")
end
avancer
-- `Current' commence un déplacement
deferred
end
arreter
-- `Current' arrête son déplacement
do
print("Arrêter le véhicule.%N")
end
end
Classe CHEVAL:
note
description: "Un animal de race chevaline dans l'animalerie."
auteur: "Louis Marchand"
date: "Mercredi, 25 Août 2021 19:29:24 +0000"
revision: "0.1"
class
CHEVAL
inherit
ANIMAL
VEHICULE
redefine
embarquer,
arreter
end
create
make
feature -- Accès
cri
-- <Precursor>
do
print("Hiiiiii.%N")
end
embarquer
-- <Precursor>
do
print("L'usager embarque sur le cheval.%N")
end
arreter
-- <Precursor>
do
print("Le cheval arrête d'avancer.%N")
end
end
Et voici maintenant un programme qui utilise ces classes:
note
description: "Exemple d'héritage multiple sans interface avec Eiffel"
auteur: "Louis Marchand"
date: "Mercredi, 25 Août 2021 19:29:24 +0000"
revision: "0.1"
class
APPLICATION
create
make
feature {NONE} -- Implémentation
make
-- Exécute l'application.
local
l_cheval:CHEVAL
l_automobile:AUTOMOBILE
do
create l_cheval.make ("Blacky", 10)
create l_automobile
demarrerTransport(l_cheval)
demarrerTransport(l_automobile)
end
demarrerTransport(a_vehicule:VEHICULE)
-- Démarre `a_vehicule'
do
a_vehicule.embarquer
a_vehicule.avancer
end
end
Auteur: Louis Marchand
Sauf pour les sections spécifiées autrement, ce travail est sous licence Creative Commons Attribution 4.0 International.