web-dev-qa-db-fra.com

Quel est l'intérêt d'avoir une interface pour chaque classe de service?

Dans l'entreprise où je travaille, chaque classe de service a une interface correspondante.
Est-ce nécessaire?

Remarques:

  • La plupart de ces interfaces ne sont utilisées que par un classe unique
  • Nous ne créons aucune sorte d'API publique

Avec des bibliothèques de moqueries modernes capables de se moquer de classes concrètes et des IDE capables d'extraire une interface d'une classe en deux clics environ, est-ce juste un maintien des temps antérieurs?

111
Bob Roberts

Votre entreprise suit les principes SOLID et le ciblage d'une interface plutôt que d'une classe concrète n'ajoute aucun frais supplémentaire au programme. Ce qu'ils font est une quantité très modérée de travail supplémentaire qui peut rapporter des volumes lorsque les hypothèses que vous faites finissent par être fausses ... et vous ne pouvez jamais dire quelle hypothèse cela va être. Ce que vous recommandez tout en travaillant un peu moins, peut coûter beaucoup, beaucoup, beaucoup d'heures de refactoring qui auraient pu être juste fait à l'avance en suivant simplement les principes de base de la conception OO.

Vous devriez apprendre de ces gars et vous considérer comme chanceux. J'aimerais avoir travaillé dans un tel environnement où un design solide était un objectif prioritaire.

98
Edward Strange

Compte tenu de votre question, je suppose que les raisons de ce type de conception ne sont pas documentées.

L'utilisation injustifiée d'une interface avec une seule implémentation est tout à fait erronée car cela viole YAGNI . D'après mon expérience, cela a également été assez dommageable pour la maintenance, principalement parce que les méthodes implémentant l'interface sont forcées d'être inutilement publiques. Après avoir accumulé plus d'expérience refactoring , vous apprendrez probablement à apprécier le charme modeste de privé et package privé modificateurs d'accès permettant un pour concentrer l'attention au sein d'une seule classe/package.

Quant à l'utilisation non documentée justifiée de cet idiome, c'est à mes yeux à peu près aussi mauvaise - tout d'abord parce qu'elle ne permet pas à un responsable de "dire bon de mauvais ". Par conséquent, cela laisse entre des options pas très attrayantes. Les options sont, soit vous gardez les trucs publics douteux tels quels, donc à plusieurs reprises diminuant la productivité chaque fois que vous devez changer le code, ou vous apportez des changements risqués en "effondrant" aveuglément l'implémentation unique en une maintenance plus facile POJO - s'exposant au risque de découvrir douloureusement que c'est la mauvaise façon, que certains autres ( Dieu ne plaise, à distance ) module hors de votre vue attend l'interface.

J'ai vécu plusieurs fois des "révélations" causées par l'effondrement erroné d'une interface à implémentation unique. Chaque fois, ça a été une expérience assez éducative mais je ne voudrais pas vraiment y retourner. Le fait est que cela se produit à des tests tardifs, à l'intégration ou même à l'acceptation des utilisateurs et que l'annulation/la correction de l'erreur commise il y a longtemps à ce stade est assez lourde.


  • Une option qui ne devrait pas être laissée sans mentionner est de rechercher si l'utilisation non documentée est justifiée (et, ensuite, s'effondrer ou documenter respectivement) juste au moment où vous la trouvez.

    Pour moi, celui-ci a été assez contre-productif. Cette approche oblige essentiellement à effectuer des tests d'intégration complets, ce qui est probablement l'un des moyens les plus efficaces pour casser un flux au milieu d'une correction/refactorisation compliquée.

    http://i.stack.imgur.com/tBUBG.jpg

Je ne pouvais tout simplement pas voir le côté pratique ... les interfaces sont utilisées de manière individuelle comme com.company.service.foo et com.company.service.foo.impl

Vous trouverez ci-dessous comment je gère généralement des cas comme celui-ci.

  1. Créez un ticket dans issue tracker , comme "PRJ-123 Utilisation douteuse non documentée de l'interface avec une seule implémentation dans Foo/FooImpl ".
    Ajoutez à la description du ticket ou pour commenter une explication que cela nuit à la maintenabilité et faites remarquer que sans documentation, cela ressemble à overengineering . Ajoutez un commentaire suggérant de résoudre le problème en le "réduisant" en POJO, pour une maintenance plus facile.
  2. Attendez un peu au cas où quelqu'un viendrait expliquer les choses. J'attendrais au moins une semaine ou deux

    • Si quelqu'un explique le but, a) mettez cela sur le ticket, b) ajoutez à Foo javadocs quelque chose comme le but de l'interface expliqué dans le ticket PRJ-123, c) fermez le ticket, d) sauter les trois étapes suivantes.
    • Autrement, ...
  3. Venez au chef d'équipe ou au manager pour demander ce qu'ils penseraient si je corrige le ticket comme suggéré.
    Choisissez un conseiller disposant des pouvoirs suffisants pour passer le "test des cris" au cas où la solution suggérée se révélerait incorrecte.
  4. Réduisez à POJO selon la correction suggérée, arrangez révision du code si c'est possible (dans des cas glissants comme ça, cela ne fera pas de mal de couvrir votre dos).
  5. Attendez que la modification réussisse le test complet et mettez à jour le ticket en fonction de son résultat - ajoutez les informations indiquant si la modification a réussi le test ou non.
  6. Créez et gérez un nouveau ticket de suivi des problèmes pour traiter les cas similaires restants en fonction du résultat de votre ticket pilote (PRJ-123): documenter le but ou réduire à POJO.
84
gnat

Voici comment Adam Bien pensez à cette question: Service s = new ServiceImpl () - Pourquoi faites-vous cela?

Ce n'est pas seulement un ballonnement (et l'opposé de l'idée de "Convention sur la configuration"), mais cela cause de réels dégâts:

  1. Imaginez que vous obteniez une autre implémentation (c'est tout l'intérêt d'une interface) - comment la nommeriez-vous?
  2. Il double la quantité d'artefacts - et augmente considérablement la "complexité"
  3. C'est vraiment drôle de lire les javadocs sur l'interface et l'impl - une autre redondance
  4. La navigation dans le IDE est moins fluide

et je suis d'accord avec lui.

Peut-être que vos collègues sont toujours coincés dans le temps avec J2EE, car cela était nécessaire lorsque vous travaillez avec J2EE, vous pouvez peut-être trouver sur Internet d'anciens tutoriels sur les moments difficiles dans J2EE.

40
Xoke

Les interfaces Java ne concernent pas seulement la possibilité de se moquer (bien sûr, c'est un facteur). Une interface expose un contrat public pour votre service, sans détails d'implémentation tels que des méthodes ou des attributs privés.

Notez que "public" lors de l'utilisation de services internes (comme dans votre cas) fait référence aux utilisateurs réels desdits services, y compris - mais sans s'y limiter - d'autres programmeurs et services internes!

20
Andres F.

Une raison possible pour chaque classe de service d'avoir une interface est qu'elle rend les interactions avec des frameworks sophistiqués (par exemple, Spring, JEE) beaucoup plus simples. En effet, Java peut simplement générer des objets intercepteurs sur des interfaces (voir Java.lang.reflect.Proxy); le faire contre des classes concrètes est beaucoup plus délicat et rempli de mises en garde non évidentes lorsque vous travaillez avec plusieurs chargeurs de classe différents. Cela est d'autant plus vrai si vous travaillez avec OSGi, où ces interfaces de service finiront par être chargées à partir d'un chargeur de classe différent des implémentations de ces services. Cela (avec le tissu de découverte de services), à son tour, impose une séparation très forte entre chaque service et ses clients; les clients littéralement ne peuvent pas briser l'abstraction.

Sachez que même si un IDE peut extraire une interface d'une classe en un seul clic, cela ne signifie pas nécessairement qu'il va extraire l'interface correcte. L'intelligence est toujours nécessaire.

12
Donal Fellows

Il semble comme une surcharge d'utiliser initialement l'Interface .. mais plus tard, lorsque nous devons étendre certaines fonctionnalités, ces Interfaces aident beaucoup.

Si vous ne faites qu'une implémentation concrète, vous devez beaucoup changer l'implémentation du code. Mais en utilisant l'interface, vous avez juste besoin d'ajouter une nouvelle classe qui implémente le contrat d'Interface (et vous pouvez appeler un nouvel objet de classe à partir de la variable de référence de l'ancienne interface) .

1
kundan bora