50 votes

Est-ce la meilleure pratique d'extraire une interface pour chaque classe?

J'ai vu du code où chaque classe a une interface qu'elle implémente.

Parfois, il n'y a pas d'interface commune pour elles toutes.

Elles sont simplement là et sont utilisées à la place d'objets concrets.

Elles n'offrent pas d'interface générique pour deux classes et sont spécifiques au domaine du problème que la classe résout.

Y a-t-il une raison de faire ça?

43voto

Scott Langham Points 17447

Non.

Les interfaces sont utiles pour les classes avec un comportement complexe, et sont particulièrement pratiques si vous voulez être en mesure de créer une classe d'implémentation de mock ou fake de cette interface pour une utilisation dans les tests unitaires.

Cependant, certaines classes n'ont pas beaucoup de comportement et peuvent être traitées davantage comme des valeurs et se composent généralement d'un ensemble de champs de données. Il est peu utile de créer des interfaces pour des classes comme celles-ci car cela introduirait des frais généraux inutiles lorsqu'il n'y a pas besoin de simuler ou de fournir des implémentations alternatives de l'interface. Par exemple, considérez une classe :

class Coordinate
{
  public Coordinate( int x, int y);
  public int X { get; }
  public int y { get; }
}

Vous ne voudriez probablement pas une interface ICoordinate pour accompagner cette classe, car il est peu utile de l'implémenter d'une autre manière que simplement obtenir et définir les valeurs de X et Y.

Cependant, la classe

class RoutePlanner
{
   // Retourne une nouvelle liste de coordonnées ordonnées pour constituer le plus court chemin possible à travers toutes les coordonnées transmises.
   public List GetShortestRoute( List waypoints );
}

vous voudriez probablement une interface IRoutePlanner pour RoutePlanner car il existe de nombreux algorithmes différents qui pourraient être utilisés pour la planification d'un itinéraire.

De plus, si vous aviez une troisième classe :

class RobotTank
{
   public RobotTank( IRoutePlanner );
   public void DriveRoute( List points );
}

En donnant une interface à RoutePlanner, vous pourriez écrire une méthode de test pour RobotTank qui crée une instance avec un RoutePlanner fictif qui renvoie simplement une liste de coordonnées sans ordre particulier. Cela permettrait à la méthode de test de vérifier que le char navigue correctement entre les coordonnées sans tester également le planificateur d'itinéraire. Cela signifie que vous pouvez écrire un test qui teste simplement une unité (le char), sans tester également le planificateur d'itinéraire.

Cependant, il est assez facile d'intégrer de vraies coordonnées dans un test de cette manière sans avoir besoin de les cacher derrière une interface ICoordinate.

28voto

Adam Houldsworth Points 38632

Après avoir revisité cette réponse, j'ai décidé de la modifier légèrement.

Non, ce n'est pas une bonne pratique d'extraire des interfaces pour chaque classe. Cela peut en fait être contre-productif. Cependant, les interfaces sont utiles pour quelques raisons :

  • Support pour les tests (mocks, stubs).
  • Abstraction de l'implémentation (avancée vers IoC/DI).
  • Des choses annexes comme le support de co- et contra-variance en C#.

Pour atteindre ces objectifs, les interfaces sont considérées comme une bonne pratique (et sont en fait nécessaires pour le dernier point). En fonction de la taille du projet, vous constaterez que vous n'aurez peut-être jamais besoin de parler à une interface ou que vous extrayez constamment des interfaces pour l'une des raisons ci-dessus.

Nous maintenons une grande application, certaines parties en sont excellentes et d'autres souffrent d'un manque d'attention. Nous nous retrouvons fréquemment à refactoriser pour extraire une interface d'un type pour le rendre testable ou pour pouvoir changer les implémentations tout en réduisant l'impact de ce changement. Nous faisons également cela pour réduire l'effet de "couplage" que les types concrets peuvent accidentellement imposer si vous n'êtes pas strict sur votre API publique (les interfaces ne peuvent représenter qu'une API publique donc pour nous deviennent par nature assez strictes).

Cela dit, il est possible d'abstraire le comportement sans interfaces et possible de tester des types sans avoir besoin d'interfaces, donc elles ne sont pas une exigence pour ce qui précède. C'est juste que la plupart des frameworks/bibliothèques que vous pouvez utiliser pour vous soutenir dans ces tâches fonctionneront efficacement contre des interfaces.


Je laisserai ma vieille réponse pour le contexte.

Les interfaces définissent un contrat public. Les personnes qui implémentent des interfaces doivent mettre en œuvre ce contrat. Les consommateurs ne voient que le contrat public. Cela signifie que les détails de l'implémentation ont été abstraits du consommateur.

Une utilisation immédiate de cela de nos jours est le Test Unitaire. Il est facile de simuler, de remplacer, de fausser les interfaces.

Une autre utilisation immédiate est la Injection de Dépendance. Un type concret enregistré pour une interface donnée est fourni à un type consommant une interface. Le type ne se soucie pas spécifiquement de l'implémentation, donc il peut demander de manière abstraite l'interface. Cela vous permet de changer les implémentations sans impacter beaucoup de code (la zone d'impact est très petite tant que le contrat reste le même).

Pour de très petits projets, je tends à ne pas m'en soucier, pour des projets de taille moyenne, j'ai tendance à m'en soucier pour les éléments de base importants, et pour les grands projets, il tend à y avoir une interface pour presque chaque classe. C'est presque toujours pour soutenir les tests, mais dans certains cas de comportement injecté, ou d'abstraction du comportement pour réduire la duplication de code.

5voto

this. __curious_geek Points 23728

Il n'y a aucune raison pratique de créer des Interfaces pour chaque classe de votre projet. Ce serait exagéré. La raison pour laquelle elles doivent extraire des interfaces serait le fait qu'elles semblent implémenter un principe OOAD "Programme vers l'interface, pas vers l'implémentation". Vous pouvez trouver plus d'informations sur ce principe avec un exemple ici.

4voto

Tony Points 1012

Avoir l'interface et coder vers l'interface rend beaucoup plus facile de remplacer les implémentations. Cela s'applique également aux tests unitaires. Si vous testez du code qui utilise l'interface, vous pouvez (en théorie) utiliser un objet de simulation à la place d'un objet concret. Cela permet à votre test d'être plus focalisé et plus fin.

Il est plus courant, d'après ce que j'ai vu, de remplacer les implémentations pour les tests (simulations) que dans le code de production réel. Et oui, cela en vaut la peine pour les tests unitaires.

2voto

Dan Tao Points 60518

Il pourrait sembler ridicule, mais le bénéfice potentiel de le faire de cette manière est que si à un moment donné vous réalisez qu'il existe un meilleur moyen de mettre en œuvre une certaine fonctionnalité, vous pouvez simplement écrire une nouvelle classe qui implémente la même interface, et changer une seule ligne pour que tout votre code utilise cette classe : la ligne où la variable d'interface est assignée.

Faire de cette manière (écrire une nouvelle classe qui implémente la même interface) signifie également que vous pouvez toujours passer d'une implémentation ancienne à une nouvelle pour les comparer.

Il se peut que vous n'exploitiez jamais cette commodité et que votre produit final utilise réellement juste la classe originale écrite pour chaque interface. Si c'est le cas, tant mieux! Mais cela n'a vraiment pas pris beaucoup de temps pour écrire ces interfaces, et si vous en aviez besoin, elles vous auraient fait gagner beaucoup de temps.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X