Eh bien en fait, il faut savoir distinguer deux choses utiles qui sont différentes.
- d'une part le fait pour plusieurs classes d'avoir un ancêtre commun, sans pour autant que l'ancêtre commun ne leur impose quoi que ce soit. C'est ce que proposent les interfaces.
- d'autre part le fait pour plusieurs classes d'une même hiérarchie de partager automatiquement du code commun. C'est ce que proposent les classes parentes, dont les enfants héritent du code contenu.
Et ces deux choses utiles, on a souvent besoin des deux. Résultat ce qui arrive souvent c'est qu'on fait :
- une interface qui servira de type racine.
- une classe mère qui implémente l'interface, et qui pourra éventuellement être abstraite.
- des classes filles de la classe mère (qui héritent donc du fait que la classe mère implémente l'interface... Et de tout le reste de la classe mère).
=> Ce qui permet d'avoir les deux avantages.
Mais donc quel est l'intérêt des interfaces, ces choses dont il est garanti qu'elles sont intégralement abstraites ?
Eh bien grosso-merdo il y en a deux :
- d'abord le "concept de programmation par interface." C'est lorsqu'une partie de ton programme reçoit un objet qui implémente une certaine interface, et se sert de cet objet à travers les méthodes de l'interface.
Ça n'a l'air de rien mais du coup mais du coup, quand tu dois fournir l'objet à cette partie du programme, eh bien tu es 100% libre de fournir ce que tu veux.
Si tu connais une classe A qui implémente cette interface, et que cette classe te convient bien, tu peux fournir une instance de A.
Si tu connais plutôt une classe B, tu peux fournir une classe B.
Et si les classes qui existent déjà ne te semble pas adaptées et que tu ne vois rien de bon à reprendre d'elles, rien de t'empêche d'écrire une classe C de zéro.
Ce dernier cas autorise des choses comme un patron décorateur automatisé en utilisant une bibliothèque de gestion automatique de décorateurs, comme Spring AOP.
Ou bien de faire des tests unitaires dans lesquels tu vas tester ton bout de programme, mais pas l'objet qu'il utilise. A la place de cet objet tu fournis une façade qui implémente l'interface et qui quand on l'appelle, répond ce qu'elle est préprogrammée à répondre au lieu de faire des choses par elle-même.
Ça permet donc de choisir très librement en fonction de la situation si tu veux faire ça :
myProgramPiece.setSortingAlgorithm(new SelectionSort());
ou ça :
myProgramPiece.setSortingAlgorithm(new BubbleSort());
ou ça :
myTestedProgramPiece.setSortingAlgorithm(new FakeSortingAlgorithm());
De manière générale, programmer par interface est un outil de génie logiciel dont l'efficacité est connue des professionnels. Elle marche extrêmement bien avec Spring et c'est pas pour rien qu'il a reçu une telle adoption dès ses débuts (malgré les horribles fichiers de configuration XML).
- deuxième intérêt, la possibilité d'implémenter plusieurs interfaces par une même classe. Ce qui dans d'autres langages est le problématique héritage multiple, ici est plutôt du sous-typage multiple. Dénué de problème.
Le problème de l'héritage multiple c'est lorsque les différentes parties héritées entrent en conflit, sans même parler du triste cas de l'héritage en triangle : classe mère A. B hérite de A et C hérite aussi de A. D hérite de B et C. Concrètement, l'ordinateur ne va pas faire de miracle : l'héritage fournit des automatismes et tenter d'automatiser ce cas-là est autocontradictoire.
Alors qu'implémenter une ou plusieurs interfaces ne fait hériter d'aucun bagage : toute l'implémentation reste à faire, de manière donc parfaitement libre. Les interfaces n'ont pas de constructeur, dont on devrait donc se demander dans quel ordre les exécuter et comment gérer leurs inévitables conflits. Ni d'état interne, dont les constructeurs se doivent d'être les garants, et qui peuvent aussi se dupliquer ou se traîner comme des boulets.
- une note pour finir :
En fait, depuis Java 8 (donc un peu récent mais pas très), les interfaces ont le droit de définir du code qui pourrait être hérité en commun par les classes qui les implémentent.
C'est ce que l'on appelle les méthodes par défaut, des méthodes déclarées par des interfaces et pour lesquelles une implémentation est donnée, même si les classes qui implémentent ont toujours le droit de les redéfinir.
Ça ne permet pas autant de choses que des méthodes définies par une classe mère, parce que contrairement à une classe, une interface n'a pas d'état interne. Les méthodes par défaut ne peuvent donc rien faire d'autre qu'appeler d'autres méthodes. Ça a son utilité pour éviter de dupliquer du code, mais ça ne peut pas tout faire.
Partager