Duplicata possible:
Quand utiliser des classes abstraites au lieu des interfaces et des méthodes d'extension en C #?
Quelles sont les autres raisons d'écrire des interfaces plutôt que des classes abstraites?
Cette question m'a semblé un peu triviale aussi, jusqu'à ce que j'y réfléchisse moi-même sérieusement.
Quel est l'intérêt d'une interface Java? Est-ce vraiment la réponse de Java à l'héritage multiple?
Malgré l'utilisation des interfaces pendant un certain temps, je n'ai jamais pensé à ce que cela voulait dire. Je sais ce qu'est une interface, mais je réfléchis toujours au pourquoi.
Java permet l'héritage multiple de l'interface; l'héritage unique est pour la mise en œuvre.
Les interfaces sont importantes car elles séparent ce que fait une classe de la façon dont elle le fait. Le contrat définissant ce à quoi un client peut s'attendre laisse le développeur libre de l'implémenter comme bon lui semble, tant qu'il respecte le contrat.
Vous en voyez des exemples partout dans le JDK. Regardez le package Java.sql - il est criblé d'interfaces. Pourquoi? Ainsi, différentes bases de données relationnelles peuvent être libres d'implémenter ces méthodes pour leur produit particulier. Les clients doivent uniquement gérer les types de référence d'interface. Changer de bases de données relationnelles est aussi simple que d'échanger un JAR de pilote JDBC pour un autre. Les clients n'ont pas besoin de changer. (Tant qu'ils ne s'écartent pas du contrat.)
Les proxys dynamiques et la programmation orientée aspect dépendent des interfaces. Ils peuvent ainsi remplacer l'implémentation au moment de l'exécution.
Vous programmez contre une interface au lieu d'une implémentation concrète afin de rendre votre code flexible.
Par exemple,
Vous programmez contre IScrewDriver
pour pouvoir utiliser PhilipsScrewDriver
ou FlatScrewDriver
, selon vos besoins.
Habituellement, vous utiliserez une sorte de conteneur IoC pour "injecter" le tournevis approprié sur votre code, mais ce n'est pas vraiment nécessaire.
J'espère que les exemples vous aideront.
Quel est l'intérêt d'une interface Java?
Le point est de séparer l'API (que faire) de l'implémentation (comment le faire).
Les interfaces sont également nécessaires lors de l'utilisation de rappels, car Java ne vous permet pas de passer des références de fonction.
Est-ce vraiment la réponse de Java à l'héritage multiple?
Entre autres choses, oui. Il vous donne certaines des caractéristiques de l'IM, sans les complications désagréables.
Il existe de nombreuses raisons pour lesquelles Java a sa propre construction pour l'héritage multiple, c'est-à-dire avec les classes interface
et abstract
. En voici trois auxquels je peux penser:
Dans les langages qui n'ont pas la construction interface
, vous devez faire beaucoup de codage standard. Considérez en C++ que vous devez créer une classe qui a des fonctions virtuelles pures:
class MyInterface
{
public:
virtual void show() = 0;
virtual int number() = 0;
};
class ConcreteImpl : public MyInterface
{
public:
void show()
{
cout << "Concrete Impl here";
}
int number()
{
return 1337;
}
};
L'équivalent dans Java serait moins détaillé, principalement parce que les méthodes sont des fonctions virtuelles (remplaçables). Et il est plus évident que l'un des artefacts est l'interface.
interface MyInterface {
void show();
int number();
}
class ConcreteImpl implements MyInterface {
public void show() {
System.out.println("Concrete Impl here");
}
public int number() { return 1337; }
}
Java autorise une classe avec un mélange pur virtuel et implémentation, avec le mot clé abstract
.
L'un des problèmes associés à l'héritage multiple est le poilu problème d'hérédité du diamant . Considérez si vous avez un langage qui autorise cette chaîne de classes dans un langage tel que C++:
Pensez à un SuperBaseClass
qui a deux implémentations BaseClass
, les deux implémentant la méthode de SuperBaseClass
en tant que fonctions virtuelles. À leur tour, les deux BaseClass
sont implémentés par la classe MyImplementation
. Considérez ce qui se passera si vous appelez super()
dans MyImplementation.draw()
? Il n'est pas toujours évident de savoir ce qui se passera, même en C++, les développeurs qui l'ont conçu se retrouveront dans une session de débogage bizarre.
Il n'y a rien de mal à l'héritage du diamant, donc pour éviter les problèmes d'appeler la super-méthode, vous devez les concevoir soit en utilisant des fonctions virtuelles pures soit en les concevant de manière plus intelligente. En Java, puisque toutes les méthodes sont virtuelles par défaut, vous êtes contraint par le fait que vous ne pouvez faire qu'un héritage multiple avec interface
s car elles ne contiennent aucune implémentation (avec le mot clé implements
) . D'autres classes, même abstract
, ne peuvent être héritées que de manière unique (avec le mot clé extends
).
Si vous êtes entré dans Design Patterns by GoF , vous remarquerez qu'ils dépendent fortement des interfaces pour concevoir des systèmes extensibles. En fait, l'un des principes des modèles de conception est le suivant:
"Programmer vers une 'interface', pas une 'implémentation'." (Gang of Four 1995: 18)
L'interface au sens du GoF signifie que vous devez programmer vers une superclasse plutôt que vers l'implémentation. Un exemple serait que vous préfériez taper votre variable à Collection<T>
, Iterable<T>
Ou List<T>
Au lieu de ArrayList<T>
Dans Java si la seule méthode que vous utilisez sur l'objet est de l'itérer. Un exemple de modèle de GoF serait le modèle de stratégie où le contexte n'a pas besoin de connaître la stratégie exacte qu'il est donné pour exécuter, mais exécutera celle définie, quelle qu'elle soit.
Cela me laissait perplexe jusqu'à ce que je réalise quelque chose:
Fondamentalement, une interface est simplement une classe qui ne contient que des méthodes abstraites. En transformant une telle entité en interface, vous gagnez une certaine flexibilité que vous n'avez pas avec les classes.
Trouvé ce lien pour être vraiment utile. Quelques extraits qui avaient du sens pour moi:
... La séparation de l'interface et de l'implémentation est l'une des principales idées derrière Java en général. La Java machine virtuelle (JVM), par exemple, est un ordinateur abstrait qui définit la façon dont votre programme "s'interface" avec l'ordinateur réel sous-jacent. Une machine virtuelle Java qui s'exécute sur Windows est une implémentation de cet ordinateur abstrait. Une machine virtuelle Java qui s'exécute sur le Macintosh en est une autre. Une machine virtuelle Java qui s'exécute sur votre montre-bracelet est encore un autre
Les interfaces vous donnent plus de polymorphisme que les familles de classes héritées individuellement, car avec les interfaces, vous n'avez pas besoin de tout faire rentrer dans une seule famille de classes.
En utilisant des interfaces, vous dissociez les parties de votre système les unes des autres et générez du code plus flexible: plus facilement modifié, étendu et personnalisé.
A implements B, C
n'est pas identique à A extends B, C
Bien que cela semble assez clair, moi aussi, j'ai toujours été confus quand les gens disent que les interfaces sont une réponse au manque d'héritage multiple.
Comme ce lien montre qu'il existe des moyens de simuler l'héritage multiple tout en utilisant des interfaces. Mais l'interface n'aide pas directement à implémenter l'héritage multiple.
L'interface, je crois, est pour plusieurs sous-types. Dans l'exemple ci-dessus, A est à la fois de type B et C. Par conséquent, A peut être substitué aux membres de B ou aux membres de C (voir cet article Wikipedia ).
Comme vous pouvez avoir plusieurs implémentations d'une même interface, cela aide également au polymorphisme dynamique. Votre code dépend du type (l'interface) et non de l'implémentation (qui pourrait être substituée dynamiquement en fonction du contexte).
Oui, les interfaces sont la réponse de Java à l'héritage multiple. Il n'y a absolument rien qu'une interface puisse faire qu'une classe abstraite ne puisse pas faire, à part le fait que plusieurs interfaces peuvent participer à l'ascendance d'une classe, alors que plusieurs classes ne le peuvent pas.
Ainsi, nous utilisons une interface au lieu d'une classe abstraite afin de donner aux descendants la liberté d'étendre la classe de base qu'ils souhaitent (et d'implémenter notre interface), plutôt que de les forcer à étendre une classe de base spécifique.