Bonsoir !
Je suis relativement nouveau dans le milieu de la prog et je suis actuellement en train d'apprendre le Java. Forcément, au bout d'un moment on doit rentrer dans la Programmation orientée objet, et c'est souvent la que les choses se corsent pour les néophytes comme moi.
J'ai regardé une série de tutoriel jusqu'au point d'introduction sur les interfaces et les classes abstraites. J'ai globalement compris leurs utilités et leurs usages, mais j'ai du mal à comprendre dans quelle situation ce serait mieux d'implémenter une interface plutôt que d'étendre une classe abstraite ?
Je pense qu'avoir une réponse à cette question m'aiderait à mieux comprendre ce sujet avant de plonger plus bas dans la POO.
Merci d'avance !
Une classe abstraite est une classe, et une classe ne peut hériter d'une seule classe, peu importe elle est abstraite ou pas. En revanche, une classe peut hériter de plusieurs interfaces.
Ainsi, d'un point de vue de la conception, un héritage entre les classes est une relation de "is-a", c'est à dire une relation pour décrire le lien "être", toutefois, lors qu'on parle d'une classe qui hérite d'une interface, c'est plutôt une relation de "has-a", une composition ou bien une relation comportementale.
Ceci dit, il vaut mieux éviter l'utilisation des relations d'héritage mais privilégier les compositions (les "mixins"), car les derniers permettent d'une flexibilité à l'implémentation.
Un exemple: avec l'approche des classes, on peut dire qu'un chat est un animal, mais dans ce cas-là il est difficile de spécifier qu'il a combien de pieds, il a quelle couleur, etc.. En revanche, avec l'approche de l'interface, on peut très bien dire qu'un chat "fait partie des mammifères, fait partie des félins, fait partie des carnivores, des animaux domestiques, et cetera..."
Avant Java 8, les interfaces ne peuvent pas contenir des implémentations concrètes, à mon avis c'est la seule raison pour utiliser les classes abstraites. On est déjà à Java 18 et on peut mettre autant des implémentations qu'on veut dans une interface donc les classes abstraites ont peu d'intérêt.
Attention: ce point de vue est une idée dérivée qui est moins pointue de point de vue classique de POO donc si tu cherche une réponse au partiel de l'école ne met pas cet argument. Dans la POO purement académique et très pointue, la différence entre une classe abstraite et une interface c'est que la dernière ne peut pas contenir les implémentations.
Le 06 août 2022 à 22:09:51 :
Une classe abstraite est une classe, et une classe ne peut hériter d'une seule classe, peu importe elle est abstraite ou pas. En revanche, une classe peut hériter de plusieurs interfaces.Ainsi, d'un point de vue de la conception, un héritage entre les classes est une relation de "is-a", c'est à dire une relation pour décrire le lien "être", toutefois, lors qu'on parle d'une classe qui hérite d'une interface, c'est plutôt une relation de "has-a", une composition ou bien une relation comportementale.
Ceci dit, il vaut mieux éviter l'utilisation des relations d'héritage mais privilégier les compositions (les "mixins"), car les derniers permettent d'une flexibilité à l'implémentation.
Un exemple: avec l'approche des classes, on peut dire qu'un chat est un animal, mais dans ce cas-là il est difficile de spécifier qu'il a combien de pieds, il a quelle couleur, etc.. En revanche, avec l'approche de l'interface, on peut très bien dire qu'un chat "fait partie des mammifères, fait partie des félins, fait partie des carnivores, des animaux domestiques, et cetera..."
Avant Java 8, les interfaces ne peuvent pas contenir des implémentations concrètes, à mon avis c'est la seule raison pour utiliser les classes abstraites. On est déjà à Java 18 et on peut mettre autant des implémentations qu'on veut dans une interface donc les classes abstraites ont peu d'intérêt.
Attention: ce point de vue est une idée dérivée qui est moins pointue de point de vue classique de POO donc si tu cherche une réponse au partiel de l'école ne met pas cet argument. Dans la POO purement académique et très pointue, la différence entre une classe abstraite et une interface c'est que la dernière ne peut pas contenir les implémentations.
Merci beaucoup pour avoir pris le temps d'écrire cette réponse complète.
@Oberginee : Il faut arrêter d'utiliser les relations phylogéniques du règne animal pour illustrer la programmation orientée objet. [1]
Une interface c'est un type qui regroupe les objets qui vérifient un certain comportement.
Imaginons que tu codes un moteur d'expressions numériques. Un nombre est une expression, la somme de deux expressions est encore une expression. Une expression c'est quelque chose d'abstrait qu'on peut évaluer pour récupérer un nombre. On pourrait modéliser ça de la sorte.
interface Expr {
evaluate(): int
}
class ConstantExpr implements Expr {
constructor(int value) {
this.value = value
}
evaluate(): int {
return this.value
}
}
class SumExpr implements Expr {
constructor(Expr lhs, Expr rhs) {
this.lhs = lhs
this.rhs = rhs
}
evaluate(): int {
return this.lhs.evaluate() + this.rhs.evaluate()
}
}
Pour représenter le calcul 3+4+5 et calculer le résultat tu ferais comme suit.
expr = new SumExpr(
new ConstantExpr(3),
new SumExpr(
new ConstantExpr(4),
new ConstantExpr(5)
)
)
print(expr.evaluate())
Ici, par définition une expression est quelque chose qu'on peut évaluer. Donc on définit l'interface Expr. Un objet de type Expr est un objet qu'on peut évaluer, un objet sur lequel on peut appliquer une méthode evaluate dont l'implémentation peut changer selon l'objet en question.
Maintenant, on a dit qu'une somme de deux expressions est elle-même une expression. Donc SumExpr prend en paramètre deux expressions et s'évalue comme la somme des évaluations de ces deux expressions.
Les classes abstraites c'est juste une généralisation des classes pour avoir un héritage plus général. Ta classe abstraite va implémenter certaines méthodes et laisser certaines autres virtuelles. En principe les méthodes implémentées vont reposer sur les méthodes virtuelles. Mais de façon générale l'héritage est plutôt une mauvaise pratique et donc les classes abstraites aussi. Tu vas utiliser ça dans des situations vraiment très précises où tu veux absolument utiliser de l'héritage pour une raison ou une autre.
En résumé : une interface c'est un type, ça permet de travailler avec des objets qui peuvent être très différents les uns des autres mais sur lesquels on peut opérer d'une certaine façon. Une classe abstraite ça généralise les classes dans le cadre de l'héritage et ça permet de partager du code.
[1] https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/
Une classe abstraite est une classe, et une classe ne peut hériter d'une seule classe, peu importe elle est abstraite ou pas. En revanche, une classe peut hériter de plusieurs interfaces.
À noter quand même que c'est une différence parfaitement superficielle et spécifique à Java. On peut très bien concevoir un langage qui permet l'héritage multiple, le problème du diamant est résolu depuis bien longtemps. D'ailleurs c'est le cas du C++.
@binecointe,
Tu parles souvent de la mauvaise utilisation de l'heritage/polymorphisme. Et tu donnes des liens a des blogs comme celui que tu as mis ici. Tu connais une bonne ressource plus completes qu'on pourrait donner aux debuttants?
Le 08 août 2022 à 01:53:15 :
@binecointe,Tu parles souvent de la mauvaise utilisation de l'heritage/polymorphisme. Et tu donnes des liens a des blogs comme celui que tu as mis ici. Tu connais une bonne ressource plus completes qu'on pourrait donner aux debuttants?
J'ai pas mieux sous la main malheureusement. Faudrait chercher du côté des livres sur la programmation orientée objet d'un point de vue théorique. J'ai pas vraiment le temps de m'y consacrer.
Pas de soucis!