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.