Pour être interchangeables et testables, normalement les services avec logique doivent avoir une interface, par exemple
public class FooService: IFooService
{ ... }
Côté design, je suis d'accord avec cela, mais l'une des choses qui me dérange avec cette approche est que pour un service, vous devrez déclarer deux choses (la classe et l'interface), et dans notre équipe, normalement deux fichiers (un pour la classe et un pour l'interface). Un autre inconfort est la difficulté de navigation car l'utilisation de "Aller à la définition" dans IDE (VS2010) pointera vers l'interface (puisque les autres classes se réfèrent à l'interface), pas la classe réelle.
Je pensais qu'écrire IFooService dans le même fichier que FooService réduirait l'étrangeté ci-dessus. Après tout, IFooService et FooService sont très liés. Est-ce une bonne pratique? Y a-t-il une bonne raison pour que IFooService se trouve dans son propre fichier?
la source
Réponses:
Il ne doit pas nécessairement se trouver dans son propre fichier, mais votre équipe doit décider d'une norme et s'y tenir.
De plus, vous avez raison de dire que "Aller à la définition" vous amène à l'interface, mais si Resharper est installé, il suffit d'un clic pour ouvrir une liste des classes / interfaces dérivées de cette interface, donc ce n'est pas un gros problème. C'est pourquoi je garde l'interface dans un fichier séparé.
la source
Je pense que vous devriez les conserver dans des fichiers séparés. Comme vous l'avez dit, l'idée est de rester testable et interchangeable. En plaçant l'interface dans le même fichier que votre implémentation, vous associez l'interface à l'implémentation spécifique. Si vous décidez de créer un objet factice ou une autre implémentation, l'interface ne sera pas logiquement séparée de FooService.
la source
Selon SOLID, non seulement vous devez créer l'interface, et non seulement si elle doit être dans un fichier différent, elle doit être dans un assemblage différent.
Pourquoi? Parce que toute modification d'un fichier source qui se compile dans un assembly nécessite une recompilation de l'assembly, et toute modification d'un assembly nécessite la recompilation de tout assembly dépendant. Donc, si votre objectif, basé sur SOLID, est de pouvoir remplacer une implémentation A par une implémentation B, alors que la classe C dépendant de l'interface I ne doit pas connaître la différence, vous devez vous assurer que l'assemblage avec I en elle ne change pas, protégeant ainsi les usages.
"Mais c'est juste une recompilation", je vous entends protester. C'est possible, mais dans votre application pour smartphone, ce qui est plus facile avec la bande passante de données de vos utilisateurs; télécharger un binaire qui a changé, ou télécharger ce binaire et cinq autres avec du code qui en dépend? Tous les programmes ne sont pas écrits pour être consommés par les ordinateurs de bureau sur un réseau local. Même dans ce cas, où la bande passante et la mémoire sont bon marché, les versions de correctifs plus petites peuvent avoir de la valeur car elles sont faciles à diffuser sur l'ensemble du LAN via Active Directory ou des couches de gestion de domaine similaires; vos utilisateurs n'attendront que quelques secondes pour qu'il soit appliqué la prochaine fois qu'ils se connectent au lieu de quelques minutes pour que le tout soit réinstallé. Sans oublier que, moins il y a d'assemblages à recompiler lors de la construction d'un projet, plus vite il se construira,
Maintenant, l'avertissement: ce n'est pas toujours possible ou faisable. La façon la plus simple de le faire est de créer un projet "d'interfaces" centralisé. Cela a ses propres inconvénients; le code devient moins réutilisable car le projet d'interface ET le projet d'implémentation doivent être référencés dans d'autres applications réutilisant la couche de persistance ou d'autres composants clés de votre application. Vous pouvez surmonter ce problème en divisant les interfaces en assemblages plus étroitement couplés, mais vous avez alors plus de projets dans votre application, ce qui rend une construction complète très douloureuse. La clé est l'équilibre et le maintien de la conception à couplage lâche; vous pouvez généralement déplacer des fichiers si nécessaire, donc quand vous voyez qu'une classe aura besoin de nombreuses modifications, ou que de nouvelles implémentations d'une interface seront nécessaires régulièrement (peut-être pour interfacer avec les versions nouvellement prises en charge d'autres logiciels,
la source
Pourquoi avoir des fichiers séparés est un inconfort? Pour moi, c'est beaucoup plus propre et plus propre. Il est courant de créer un sous-dossier nommé "Interfaces" et d'y coller vos fichiers IFooServer.cs, si vous voulez voir moins de fichiers dans votre Explorateur de solutions.
La raison pour laquelle l'interface est définie dans son propre fichier est pour la même raison que les classes sont généralement définies dans leur propre fichier: la gestion de projet est plus simple lorsque votre structure logique et la structure de fichier sont identiques, de sorte que vous savez toujours quel fichier une classe donnée est définie dans. Cela peut vous faciliter la vie lors du débogage (les traces de pile d'exceptions vous donnent généralement des numéros de fichier et de ligne) ou lors de la fusion de code source dans un référentiel de contrôle de source.
la source
Il est souvent recommandé de laisser vos fichiers de code ne contenir qu'une seule classe ou une seule interface. Mais ces pratiques de codage sont un moyen d'arriver à une fin - pour mieux structurer votre code et le rendre plus facile à utiliser. Si vous, et votre équipe, trouvez plus facile de travailler avec si les classes sont maintenues avec leurs interfaces, faites-le par tous les moyens.
Personnellement, je préfère avoir l'interface et la classe dans le même fichier lorsqu'il n'y a qu'une seule classe qui implémente l'interface, comme dans votre cas.
En ce qui concerne les problèmes que vous rencontrez avec la navigation, je peux fortement recommander ReSharper . Il contient des raccourcis très utiles pour passer directement à la méthode implémentant une méthode d'interface spécifique.
la source
Il y a de nombreuses fois où vous souhaitez que votre interface soit non seulement dans un fichier séparé de la classe, mais même dans un assemblage distinct.
Par exemple, une interface de contrat de service WCF peut être partagée par le client et le service si vous contrôlez les deux extrémités du câble. En déplaçant l'interface dans son propre assembly, elle aura moins de dépendances d'assembly. Cela permet au client de consommer beaucoup plus facilement, en relâchant le couplage avec sa mise en œuvre.
la source
Il est rarement, voire jamais, logique d'avoir une seule implémentation d'une interface 1 . Si vous mettez une interface publique et une classe publique implémentant cette interface dans le même fichier, il y a de fortes chances que vous n'ayez pas besoin d'une interface.
Lorsque la classe que vous colocalisez avec l'interface est abstraite et que vous savez que toutes les implémentations de votre interface doivent hériter de cette classe abstraite, localiser les deux dans le même fichier est logique. Vous devriez toujours examiner attentivement votre décision d'utiliser une interface: demandez-vous si une classe abstraite en elle-même conviendrait bien, et supprimez l'interface si la réponse est affirmative.
Cependant, en règle générale, vous devez vous en tenir à la stratégie "une classe / interface publique correspond à un fichier": elle est facile à suivre et facilite la navigation dans votre arbre source.
1 Une exception notable est lorsque vous avez besoin d'une interface à des fins de test, car votre choix de cadre de simulation a imposé cette exigence supplémentaire à votre code.
la source
sealed
. Si vous pensez que vous pourrez à un moment donné à l'avenir ajouter une deuxième sous-classe, vous mettez immédiatement une interface. PS Un assistant de refactoring de qualité décente peut être le vôtre à un prix modique d'une seule licence ReSharper :)virtual
.Les interfaces appartiennent à leurs clients et non à leurs implémentations, telles que définies dans les principes, pratiques et modèles agiles de Robert C. Martin. Ainsi, combiner interface et implémentation au même endroit est contraire à son principe.
Le code client dépend de l'interface. Cela permet de compiler et de déployer le code client et l'interface sans l'implémentation. Vous pouvez avoir différentes implémentations fonctionnant comme des plugins pour le code client.
MISE À JOUR: Ce n'est pas un principe uniquement agile ici. Le Gang of Four in Design Patterns, en 1994, parle déjà de clients adhérant aux interfaces et programmant les interfaces. Leur point de vue est similaire.
la source
Personnellement, je n'aime pas le «je» devant une interface. En tant qu'utilisateur d'un tel type, je ne veux pas voir qu'il s'agit d'une interface ou non. Le type est la chose intéressante. En termes de dépendances, votre FooService est UNE implémentation possible d'IFooService. L'interface doit être proche de l'endroit où elle est utilisée. D'un autre côté, la mise en œuvre doit se faire à un endroit où elle peut être facilement modifiée sans affecter le client. Donc, je préférerais deux fichiers - normalement.
la source
new
avec, mais d'un autre côté, je peux l'utiliser en co ou contre-variante. Et leI
est une convention de dénomination établie dans .Net, c'est donc une bonne raison d'y adhérer, ne serait-ce que pour la cohérence avec d'autres types.I
dans l'interface n'était que l'introduction à mes arguments sur la séparation dans deux fichiers. Le code est mieux lisible et rend plus claire l'inversion des dépendances.I
est normale dans votre environnement: utilisez-le! (Mais je n'aime pas ça :))Le codage des interfaces peut être mauvais, est en fait traité comme un anti-modèle pour certains contextes et n'est pas une loi. Habituellement, un bon exemple de codage des interfaces anti-modèle est lorsque vous avez une interface qui n'a qu'une seule implémentation dans votre application. Un autre serait si le fichier d'interface devient si gênant que vous devez cacher sa déclaration dans le fichier de la classe implémentée.
la source
Mettre une classe et une interface dans le même fichier ne met aucune limite sur la façon dont l'une ou l'autre peut être utilisée. Une interface peut toujours être utilisée pour les simulations, etc. de la même manière, même si elle se trouve dans le même fichier qu'une classe qui l'implémente. La question pourrait être perçue uniquement comme une question de commodité organisationnelle, auquel cas je dirais faire tout ce qui vous facilite la vie!
Bien sûr, on pourrait faire valoir que le fait d'avoir les deux dans le même fichier pourrait conduire les imprudents à les considérer comme étant plus couplés par programme qu'ils ne le sont réellement, ce qui constitue un risque.
Personnellement, si je tombais sur une base de code qui utilisait une convention sur l'autre, je ne serais pas trop inquiet de toute façon.
la source