L'héritage de classe et les interfaces ont tous deux leur place. Héritage signifie "est un" alors qu'une interface fournit un contrat qui définit ce à quoi "se comporte".
Je dirais que l'utilisation d'interfaces plus souvent n'est pas une mauvaise pratique du tout. Je suis en train de lire "Effective C # - 50 moyens spécifiques d'améliorer votre C #" de Bill Wagner. Le numéro d'article 22 indique, et je cite, "Préférer la définition et la mise en oeuvre d'interfaces à l'héritage".
En général, j'utilise des classes de base lorsque je dois définir une implémentation spécifique du comportement commun entre types apparentés. Plus souvent, j'utilise des interfaces. En fait, je commence normalement par définir une interface pour une classe lorsque je commence à en créer une ... même si au final je ne compile pas l'interface, je trouve qu'il est utile de commencer par définir l'API publique de la classe. classe dès le départ. Si je constate que plusieurs classes implémentent l'interface et que la logique d'implémentation est identique, ce n'est qu'alors que je me demanderai s'il serait judicieux d'implémenter une classe de base commune entre les types.
Quelques citations du livre de Bill Wagners ...
toutes les classes dérivées incorporent immédiatement ce comportement. L'ajout d'un membre à une interface annule toutes les classes qui implémentent cette interface. Ils ne contiendront pas la nouvelle méthode et ne compileront plus. Chaque implémenteur doit mettre à jour ce type pour inclure le nouveau membre. Choisir entre une classe de base abstraite et une interface dépend de la meilleure façon de prendre en charge vos abstractions au fil du temps. Les interfaces sont corrigées: vous publiez une interface sous forme de contrat pour un ensemble de fonctionnalités que tout type peut implémenter. Les classes de base peuvent être étendues avec le temps. Ces extensions font partie de chaque classe dérivée. Les deux modèles peuvent être combinés pour réutiliser le code d'implémentation tout en prenant en charge plusieurs interfaces. " Ils ne contiendront pas la nouvelle méthode et ne compileront plus. Chaque implémenteur doit mettre à jour ce type pour inclure le nouveau membre. Choisir entre une classe de base abstraite et une interface dépend de la meilleure façon de prendre en charge vos abstractions au fil du temps. Les interfaces sont corrigées: vous publiez une interface sous forme de contrat pour un ensemble de fonctionnalités que tout type peut implémenter. Les classes de base peuvent être étendues avec le temps. Ces extensions font partie de chaque classe dérivée. Les deux modèles peuvent être combinés pour réutiliser le code d'implémentation tout en prenant en charge plusieurs interfaces. " Ils ne contiendront pas la nouvelle méthode et ne compileront plus. Chaque implémenteur doit mettre à jour ce type pour inclure le nouveau membre. Choisir entre une classe de base abstraite et une interface dépend de la meilleure façon de prendre en charge vos abstractions au fil du temps. Les interfaces sont corrigées: vous publiez une interface sous forme de contrat pour un ensemble de fonctionnalités que tout type peut implémenter. Les classes de base peuvent être étendues avec le temps. Ces extensions font partie de chaque classe dérivée. Les deux modèles peuvent être combinés pour réutiliser le code d'implémentation tout en prenant en charge plusieurs interfaces. " Vous publiez une interface sous forme de contrat pour un ensemble de fonctionnalités que tout type peut implémenter. Les classes de base peuvent être étendues avec le temps. Ces extensions font partie de chaque classe dérivée. Les deux modèles peuvent être combinés pour réutiliser le code d'implémentation tout en prenant en charge plusieurs interfaces. " Vous publiez une interface sous forme de contrat pour un ensemble de fonctionnalités que tout type peut implémenter. Les classes de base peuvent être étendues avec le temps. Ces extensions font partie de chaque classe dérivée. Les deux modèles peuvent être combinés pour réutiliser le code d'implémentation tout en prenant en charge plusieurs interfaces. "
"Les interfaces de codage offrent une plus grande flexibilité aux autres développeurs que le codage des types de classes de base."
"L'utilisation d'interfaces pour définir des API pour une classe offre une plus grande flexibilité."
"Lorsque votre type expose des propriétés en tant que types de classe, l'interface entière est exposée à cette classe. À l'aide d'interfaces, vous pouvez choisir d'exposer uniquement les méthodes et les propriétés que les clients doivent utiliser."
"Les classes de base décrivent et implémentent des comportements communs entre types concrets associés. Les interfaces décrivent des fonctionnalités atomiques que des types concrets non liés peuvent implémenter. Les deux ont leur place. Les classes définissent les types que vous créez. Les interfaces décrivent le comportement de ces types en tant que fonctionnalités. Si vous comprenez les différences, vous créerez des conceptions plus expressives et plus résilientes face au changement. Utilisez des hiérarchies de classes pour définir les types associés. Exposez la fonctionnalité à l'aide d'interfaces implémentées entre ces types. "