Quand dois-je utiliser une interface en Java?

Un bon exemple de l’utilisation exacte d’interfaces spécifiquement en Java serait l’idéal et les règles spécifiques applicables.

Utilisez des interfaces pour définir un contrat de programmation d’application (schéma directeur, interface) que les fournisseurs tiers doivent respecter et implémenter intégralement. De cette façon, les utilisateurs finaux peuvent simplement coder contre le contrat API et basculer facilement de la mise en œuvre concrète “sous les capots” sans changer le code.

L’ API JDBC est un excellent exemple. Il existe presque uniquement des interfaces. Les implémentations concrètes sont fournies en tant que “pilotes JDBC”. Cela vous permet d’écrire tout le code JDBC indépendamment du fournisseur de firebase database (DB). Vous pouvez simplement modifier le pilote JDBC sans modifier aucune ligne de code Java (à l’exception de tout code SQL spécifique à la firebase database codé en dur) chaque fois que vous souhaitez changer de fournisseur de firebase database.

Un autre exemple est l’ API Java EE , qui contient également des interfaces et des classes abstraites. Les implémentations concrètes sont fournies sous la forme de “serveurs d’application Java EE”, “Servletcontainers”, etc., tels que Sun Glassfish, Apache Tomcat, etc.

Un bon endroit serait le cadre des collections.

 java.util.List //interface java.util.ArrayList //Concrete class java.util.LinkedList //Concrete class 

Vous pouvez donc écrire du code comme ceci:

 List l = new ArrayList(); l.add(..) //do something else. 

Si à l’avenir vous souhaitez modifier l’implémentation avec, par exemple, LinkedList ou AwesomeList which implements List interface AwesomeList which implements List , il vous suffit de modifier la toute première ligne pour:

 List l = new MyAwesomeList(); or List l = new LinkedList(); 

Le rest du code suivrait.

Les interfaces sont nécessaires lorsque vous prévoyez de la volatilité dans votre programme, des points auxquels vous prévoyez des changements, des points sur lesquels votre conception doit se plier.

La mise en œuvre est fragile en ce sens: elle se casse assez facilement. C’est pourquoi le sous-classement n’est pas toujours la meilleure solution, de même que les méthodes à long terme qui implémentent des comportements compliqués sont généralement une mauvaise idée.

Les interfaces sont plus flexibles et peuvent gérer beaucoup plus la conception de votre programme que la mise en œuvre.

En introduisant des interfaces dans votre programme, vous introduisez vraiment des points de variation auxquels vous pouvez connecter différentes implémentations pour cette interface. Le but premier des interfaces est l’ abstraction , découplant le “quoi” du “comment”.

Le principe de substitution de Liskov [ UncleBob , Wikipedia ] est une règle importante à suivre. Alors qu’un compilateur dans un langage comme Java s’assurera que tout est syntaxiquement correct (bon nombre de parameters, types, …), LSP traite de la sémantique . En résumé, LSP dit que toute implémentation d’une interface doit (aussi) se comporter correctement pour être véritablement substituable comme décrit ci-dessus.

De la page de documentation d’Oracle

Envisagez d’utiliser des interfaces si:

  1. Vous vous attendez à ce que des classes non liées implémentent votre interface. Par exemple, de nombreux objects non liés peuvent implémenter une interface Serializable.
  2. Vous souhaitez spécifier le comportement d’un type de données particulier, mais ne vous préoccupez pas de savoir qui implémente son comportement.
  3. Vous voulez profiter de l’inheritance multiple de type.

Jetez un coup d’oeil aux questions SE associées avec des exemples de code qui ont déjà de bonnes réponses.

Y a-t-il plus d’une interface que d’avoir les méthodes correctes

Quelle est la différence entre une interface et une classe abstraite?

Comment devrais-je expliquer la différence entre une interface et une classe abstraite?

Essayez de comprendre le modèle de conception de stratégie

Les interfaces sont utilisées lorsque vous avez besoin de plusieurs implémentations du même comportement. Voici un exemple d’interface que les objects pourraient implémenter pour montrer qu’ils peuvent tous être sérialisés en XML.

 public interface Xmlizable { public Ssortingng toXML(); } 

alors vous pouvez simplement faire circuler des interfaces “Xmlizable” dans des méthodes qui ne concernent que cette interface.

Jetez un coup d’œil au tutoriel de la collection JDK sur le texte du lien . Pensez aux collections. Qu’est-ce qui vous vient à l’esprit? Il pourrait être commandé ou il ne peut pas être commandé et il peut avoir des doublons ou non.

Donc, Collection est une interface avec List (ordonnée) et Set (non ordonnée) en tant que sous-interfaces. Maintenant, il y a beaucoup de questions avec les listes, si elles doivent être synchronisées ou non, s’il s’agit d’une liste chaînée ou non, etc. Chaque “comportement” aura ses propres interfaces / classes abstraites.

Les classes abstraites sont nécessaires lorsque vous souhaitez spécifier “certains” comportements dans les collections. Par exemple, toutes les collections (sets / lists, etc.) peuvent avoir une représentation “toSsortingng” qui consiste simplement à parcourir les éléments (ordonnés ou non) et à les limiter. Ce comportement peut être présent dans “AbstractCollection”, etc.

Si vous suivez la hiérarchie des collections JDK, c’est un excellent endroit pour en apprendre davantage sur les interfaces et les classes abstraites 🙂

Fondamentalement, vous pouvez choisir entre les interfaces et les classes abstraites lorsque vous devez “omettre” certains détails d’implémentation. L’interface est généralement le meilleur choix, car les classes client peuvent implémenter n’importe quelle quantité d’interfaces, mais elles ne peuvent en avoir qu’une seule (“l’inheritance est une ressource rare”, comme elles le disent).

Pourquoi voudriez-vous soit une classe abstraite ou une interface? Parce que parfois, lorsque vous écrivez un algorithme, vous ne vous souciez pas de la façon dont une sous-étape spécifique est effectuée, mais que cela se fait selon un contrat. Un exemple serait l’API Collections, avec List étant une interface – en général, lorsque vous utilisez une List , vous ne vous souciez pas vraiment de savoir si elle enregistre des éléments dans un tableau, ou dans une liste de nœuds façon. Tant que vous stockez ce que vous y mettez dans l’ordre où vous les avez mis, vous êtes heureux.

Ensuite, nous avons AbstractList : une classe abstraite implémentant List , qui fournit l’implémentation de presque tout ce dont une List part entière a besoin – pour créer votre propre implémentation List , il suffit d’étendre AbstractList et de remplir quelques méthodes. Ceci est un exemple parfait de cas où une classe abstraite est un bon choix – lorsque vous voulez fournir une implémentation presque complète de quelque chose, qui ne manque que de quelques lacunes qui doivent être remplies par le code client.

Conseil: Si vous créez une classe abstraite contenant uniquement des méthodes abstraites, vous devriez probablement en faire une interface.

Cette réponse est essentiellement la même que celle de coolest_head, un peu plus explicite quant à l’utilité.

Comme l’explique coolest_head, les interfaces sont utiles lorsque vous souhaiterez éventuellement contourner les sous-composants possibles de votre programme. Ils vous permettent également de séparer plus facilement les préoccupations de différentes parties de la structure de votre programme, car avec les interfaces, vous pouvez vous assurer que certaines classes non liées, etc., ne sont tout simplement pas visibles par les autres parties du programme.

Par exemple, supposons que vous souhaitiez lire des données arbitraires et les imprimer, comme ceci:

 SomeReader someReader = new SomeReader(); Ssortingng data = someReader.readLine(); System.out.println(data); 

Rien de spécial ici, non? Mais, bien que cet exemple soit simple, il est déjà lié à la classe SomeReader , ce qui signifie que toutes les modifications que vous apportez à cette classe doivent être propagées à la classe où vous utilisez la classe, surtout si vous refactorez certaines parties internes! Au lieu de cela, vous voulez faire cela

 IMyReader reader = new SomeReader(); System.out.println(reader.readLine()); 

Vous y êtes presque – maintenant le code d’impression ne se soucie plus de l’implémentation spécifique, mais uniquement des parties exposées par l’interface. Ceci est généralement suffisant car vous pouvez maintenant changer cette new instruction et obtenir de nouvelles implémentations et tout ce qui fonctionne comme prévu ( tant que le contrat de l’interface est respecté parmi les classes d’implémentation! ). C’est particulièrement pratique lorsque vous utilisez cet object spécifique plusieurs fois – ici, je ne l’utilise qu’une fois, mais en réalité, si vous travaillez avec des listes, par exemple, combien d’opérations faites-vous habituellement avec la même liste?

Donc, pour vraiment faire exploser cet exemple, voici à quoi pourrait ressembler votre code

 public class RowPrinter { private final IMyReader reader; public RowPrinter(IMyReader reader) { this.reader = reader; } public void print() { IMyReader reader = getReader(); System.out.println(reader.readLine()); } protected IMyReader getReader() { return reader; } } 

Notez cette partie avec le constructeur? C’est l’ inversion du contrôle et laissez-moi juste vous dire, c’est une bonne partie du génie logiciel là-bas. Je peux parler par expérience, cela vous aide beaucoup, que ce soit le passage d’un produit de firebase database à un autre ou la sécurisation de certaines parties du fil de code. Ou peut-être voulez-vous simplement append une couche de journalisation à une classe, facilement faisable avec un décorateur d’ encapsulation qui implémente la même interface que la classe encapsulée. Et ce n’est que le début.

Les interfaces apportent de nombreux avantages qui ne sont généralement pas évidents à partir d’exemples simples, bien que les exemples simples vous permettent de bien démarrer. Alors que les interfaces en Java sont une construction de langage, elles sont en réalité plus un paradigme de programmation qu’une fonctionnalité d’un seul langage. Dans certaines langues, l’émulation d’interfaces est vraiment bénéfique si vous déterminez la manière correcte de le faire.

Un principe de base de la POO est le masquage des informations: masquez les détails de l’implémentation et affichez uniquement une description des services de base à l’appelant.

Java doit construire pour cet objective: interfaces et classes abstraites. Vous pouvez définir une interface et écrire votre code, cela ne dépend que de l’interface en appelant les “méthodes disponibles” qui y sont définies.

Le masquage des informations est utilisable à la fois pour la lecture des classes externes (externe dans le sens où il est extérieur au module que vous écrivez) – vous pouvez ainsi définir les méthodes dont vous avez besoin définir un type de données utilisable en dehors de vos classes – un exemple typique est par exemple l’API Collection ou J2EE, comme d’autres déjà mentionné.

Les deux interfaces et les classes abstraites fournissent ces détails – mais il existe deux différences majeures: les interfaces prennent en charge l’inheritance multiple, tandis que les classes abstraites peuvent contenir une implémentation de base. Pour optimiser leur efficacité, lors de la définition d’une interface, vous devez également définir une classe abstraite avec une implémentation par défaut significative. Si l’utilisateur de l’interface n’a pas besoin d’étendre une autre classe, il peut étendre cette classe abstraite, sinon il doit implémenter toutes les méthodes de l’interface. D’autre part, assurez-vous de ne pas lire directement cette classe abstraite, l’interface devrait être suffisante en tant qu’abstraction.

Interfaces en communication à distance:

Les interfaces peuvent également être utilisées pour définir un “protocole” convenu pour la communication entre différentes parties du système, éventuellement par le biais d’appels distants. Ainsi, l’interface ne définit que ce qui peut être appelé, avec quels parameters et ce qui sera renvoyé après l’appel. Le client utilise l’interface et le serveur implémente le code concret par exemple.

Note latérale:

En Java, vous pouvez uniquement intégrer (étendre) une classe, mais vous pouvez implémenter plusieurs interfaces. Vous devrez donc parfois utiliser des interfaces lorsque vous avez besoin de ce type d’inheritance multiple et lorsque vous décidez de ne pas utiliser la composition par inheritance.

Utilisez des interfaces chaque fois que vous envisagez de remplacer une classe d’implémentation par une autre classe à un stade de développement.

Je recommande également d’utiliser des backends d’interface pour toutes les classes dans les relations d’inheritance au moins dans des projets plus sérieux: Malheureusement, je n’ai plus le lien mais un développeur du langage Java a dit que l’inclusion de Class-Inherit était la plus grande erreur concevoir la langue.

Les arguments sont plutôt bons: en utilisant une conception correcte, il est toujours possible de remplacer l’inheritance de classes par un inheritance d’interface et vous gagnez beaucoup en maintenance de code. Il est également plus facile de préserver les relations de type naturel (comme par exemple la géomésortinge (“un carré est un rectangle”) que l’inheritance de classe.