web-dev-qa-db-fra.com

Les interfaces doivent-elles être placées dans un paquet séparé?

Je suis nouveau dans une équipe travaillant sur un projet plutôt volumineux, avec beaucoup de composants et de dépendances. Pour chaque composant, il existe un package interfaces dans lequel les interfaces exposées de ce composant sont placées. Est-ce une bonne pratique?

Ma pratique habituelle a toujours été les interfaces et les implémentations vont dans le même paquet.

67
kctang

Placer à la fois l'interface et la mise en œuvre est un lieu commun et ne semble pas poser de problème.

Prenons l'exemple de l'API Java: la plupart des classes ont les deux interfaces et leurs implémentations incluses dans le même package. 

Prenons par exemple le paquet Java.util :

Il contient les interfaces telles que Set, Map, List, tout en ayant les implémentations telles que HashSet, HashMap et ArrayList.

En outre, les Javadocs sont conçus pour fonctionner correctement dans ces conditions, car ils séparent la documentation entre les vues Interfaces et Classes lors de l'affichage du contenu du package.

Avoir des paquets uniquement pour les interfaces peut en réalité être un peu excessif, à moins qu'il y ait un nombre énorme d'interfaces. Mais séparer les interfaces dans leurs propres paquets, rien que pour le faire, semble être une mauvaise pratique.

S'il est nécessaire de différencier le nom d'une interface d'une implémentation, vous pouvez avoir une convention de dénomination facilitant l'identification des interfaces:

  • Préfixe le nom de l'interface avec une I. Cette approche est adoptée avec les interfaces du framework .NET. Il serait assez facile de dire que IList est une interface pour une liste.

  • Utilisez le suffixe -able. Cette approche est souvent utilisée dans l'API Java, telle que Comparable, Iterable et Serializable, pour n'en nommer que quelques-unes.

85
coobird

Pour toutes les langues, les mettre ensemble dans le même paquet est parfait. L'important est ce qui est exposé au monde extérieur et à quoi il ressemble. Personne ne saura ni ne se souciera de savoir si la mise en œuvre est dans le même package ou non.

Regardons ce cas particulier.

Si vous avez toutes les choses publiques dans un paquet et les choses privées dans un autre paquet qui n'est pas exposé publiquement, le client de la bibliothèque voit un paquet. Si vous déplacez les éléments privés vers le package avec les éléments exposés publiquement, mais ne les exposez pas à l'intérieur du package, le client voit exactement la même chose.

Cela a donc l’odeur d’une règle sans raison valable: c’est prendre une décision en fonction de quelque chose qui est visible publiquement sans que cette décision n’affecte en rien ce qui est visible publiquement.

Cela dit, si dans un cas particulier, il semble judicieux de scinder l'interface et l'implémentation en plusieurs packages, continuez et faites-le. La raison qui me vient à l’esprit est que le paquet est énorme ou que vous avez peut-être une autre implémentation que vous voudrez peut-être lier à la place de celle standard.

17
Curt J. Sampson

Dans de nombreux frameworks, tels que OSGi, vous devez presque le faire. Je pense que cela favorise un couplage plus lâche, au niveau de l'emballage au lieu du niveau du récipient.

9
javamonkey79

L'un des arguments en faveur de l'intégration d'interfaces dans différents packages est qu'il est plus facile de créer des fichiers jar 'api' pouvant être distribués aux consommateurs de votre produit ou service. Il est parfaitement possible de faire cela avec les interfaces et les implémentations ensemble, mais un script plus simple s’ils sont dans des packages différents.

7
Cameron Pope

Le livre Génie logiciel pratique: une approche d’étude de cas préconise de placer des interfaces dans des projets/packages distincts. 

L'architecture PCMEF + dont il est question dans le livre repose sur les principes suivants:

  1. Principe de dépendance descendante (DDP)
  2. Principe de notification ascendante (UNP)
  3. Principe de communication du voisin (NCP)
  4. Principe d'association explicite (PAE)
  5. Principe d’élimination du cycle (CEP)
  6. Principe de nommage de classe (CNP)
  7. Principe du forfait de connaissance (APP)

La description des principes 3 et 7 explique pourquoi c'est une bonne idée:

Le principe de communication avec le voisin exige qu'un paquet ne puisse être utilisé que communiquer directement avec son paquet voisin. Ce principe assure que le système ne se désintègre pas en un réseau incompressible de objets intercommunicants. Pour appliquer ce principe, le message passe entre objets non voisins utilise la délégation (Section 9.1.5.1). Dans scénarios plus complexes, un package de connaissance (section 9.1.8.2) peut être utilisé pour grouper des interfaces pour aider à la collaboration qui engage forfaits lointains.

Le principe du forfait connaissance est la conséquence du voisin Principe de communication. Le paquet de connaissances se compose de interfaces qu'un objet passe, au lieu d'objets concrets, dans arguments aux appels de méthodes. Les interfaces peuvent être implémentées dans n’importe quel fichier Paquet PCMEF. Cela permet effectivement la communication entre forfaits non voisins tout en centralisant la gestion des dépendances vers un forfait connaissance unique. Le besoin de connaissance paquet était expliqué à la section 9.1.8.2 et est à nouveau traité dans PCMEF le contexte.

Voir ce lien: http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf

5
Kenci

Oui, c'est une très bonne pratique, car cela vous permet de publier l'interface sans publier votre implémentation spécifique. Cela dit, si vous n’avez pas besoin de publier des interfaces externes, le fait de placer les définitions d’interface dans le même package que l’implémentation ne pose aucun problème.

4
Paul Sonier

Nous le faisons là où je travaille (par exemple: mettre l'interface dans un paquet et l'implémentation dans un autre) et le principal avantage que nous en tirons est que nous pouvons facilement permuter les implémentations.

3
hhafez

Je fais cela sur un projet et cela fonctionne bien pour moi pour ces raisons:

  • Les interfaces et les implémentations empaquetées séparément sont destinées à un cas d'utilisation différent de celui utilisé pour les différents types de carte ou d'ensemble. Il n'y a aucune raison d'avoir un package uniquement pour les arbres (Java.util.tree.Map, Java.util.tree.Set). Il ne s'agit que d'une structure de données standard, alors associez-la aux autres infrastructures de données. Cependant, si vous avez affaire à un jeu de puzzle doté d'une interface de débogage très simple et d'une jolie interface de production, vous aurez peut-être un com.your.app.skin.debug et un com.votre.app. .skin.pretty. Je ne mettrais pas ceux-ci dans le même paquet, car ils font des choses différentes et je sais que je recourrais à une convention SmurfNaming (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface, etc.) pour créer un espace de noms informel pour les deux s'ils étaient dans le même paquet.

  • Pour résoudre le problème de la recherche d'interfaces et d'implémentations associées dans des packages distincts, ma solution consiste à adopter une configuration de dénomination pour mes packages. Par exemple. Je peux avoir toutes mes interfaces dans com.votre.app.skin.framework et savoir que d'autres packages situés au même niveau de l'arbre de package sont des implémentations. L'inconvénient est qu'il s'agit d'une convention non conventionnelle. Honnêtement, je vais voir à quel point cette convention est bonne dans 6 mois :)

  • Je n'utilise pas cette technique religieusement. Il existe des interfaces qui n'ont de sens que dans une implémentation particulière. Je ne les colle pas dans le package framework. Il y a des paquets pour lesquels il ne semble pas que je vais créer 40 classes d'implémentation différentes, alors je ne me dérange pas.

  • Mon application utilise guice et possède de très nombreuses interfaces.

Les questions relatives à la conception des programmes comportent généralement des avantages et des inconvénients et il n’ya pas de solution unique. Par exemple, pourquoi utiliseriez-vous cette technique dans un petit programme de 200 lignes? Compte tenu de mes autres choix architecturaux, cela a du sens pour mon problème spécifique. :)

2
Moray Jaundice

Je n’ai pas beaucoup d’expérience Java, mais j’aime bien faire cela comme bonne pratique en C #/.NET car cela permet une expansion future où les assemblys avec les classes concrètes qui implémentent les interfaces risquent de ne pas être distribués jusqu’au client parce qu’ils sont mandatés par une usine intermédiaire ou par réseau dans un scénario de service Web.

2
Jesse C. Slicer

Je mets normalement des interfaces avec l'implémentation, mais je peux en quelque sorte comprendre pourquoi vous pouvez les garder séparées. Supposons, par exemple, que quelqu'un veuille réimplémenter des classes en fonction de vos interfaces. Il aurait besoin d'un jar/lib/etc avec votre implémentation plutôt que de simplement les interfaces. Avec eux séparés, vous pouvez simplement dire "Voici mon implémentation de cette interface" et en finir. Comme je l'ai dit, ce n'est pas ce que je fais mais je peux en quelque sorte comprendre pourquoi certains pourraient vouloir.

1
Matt_JD

Je les préfère dans le même paquet. Cela ne sert à rien de créer un paquet spécialement pour les interfaces. Ceci est particulièrement redondant pour les équipes qui aiment simplement leurs préfixes et suffixes d’interface (par exemple, le "I" et "Impl" pour Java).

S'il est nécessaire de publier un ensemble d'interfaces en tant qu'API publique, il est plus logique de les conserver dans un projet entièrement séparé et de créer des dépendances de projet. Mais tout dépend de la préférence et de la commodité de la situation, je suppose.

0
aberrant80

Je pense qu'il est important de noter que pour le framework OSGi, il est préférable qu'ils soient dans des packages différents afin que vous puissiez facilement exporter un package complet tout en masquant les packages de mise en œuvre.

0
jkabugu

Mettez-les dans des paquets qui reflètent vos projets. Il est courant et courant de réunir des interfaces et des implémentations si elles font partie du même projet, mais si vous écrivez une API, quelqu'un d'autre choisirait probablement un nom de package correspondant à son projet.

En général, si cela concerne le même projet, je ne vois aucun avantage à conserver les interfaces dans un package distinct de leurs imples. S'il y a encombrement, il peut y avoir d'autres problèmes de dénomination des paquets, indépendamment de la disposition de l'interface.

0
Rog