Quand utiliser des interfaces ou des classes abstraites? Quand utiliser les deux?

alors que certaines lignes directrices indiquent que vous devez utiliser une interface lorsque vous voulez définir un contrat pour une classe où l'héritage n'est pas clair ( IDomesticated ) et l'héritage lorsque la classe est une extension d'une autre ( Cat : Mammal , Snake : Reptile ), il y a des cas où (à mon avis) ces lignes directrices entrent dans une zone grise.

par exemple, dire que ma mise en œuvre était Cat : Pet . Pet est une classe abstraite. Si cela devait être étendu à Cat : Mammal, IDomesticatedMammal est une classe abstraite et IDomesticated une interface? Ou suis-je en conflit avec les principes KISS / YAGNI (même si Je ne suis pas sûr qu'il y aura une classe Wolf dans le futur, qui ne serait pas en mesure d'hériter de Pet )?

S'éloignant de la métaphorique Cat s et Pet s, disons que j'ai quelques classes qui représentent des sources pour les données entrantes. Ils ont tous besoin de mettre en œuvre la même base d'une façon ou d'une autre. Je pourrais implémenter un code générique dans une classe abstraite Source et en hériter. Je pourrais aussi juste faire une interface ISource (qui me semble plus" juste") et ré-implémenter le code générique dans chaque classe (qui est moins intuitif). Enfin, je pourrais "avoir le gâteau et le manger" en faisant à la fois la classe abstraite et l'interface. Quel est le meilleur?

ces deux cas soulèvent des points pour l'utilisation classe abstraite, seulement une interface et une classe abstraite et une interface. Ces choix sont-ils tous valables, ou y a-t-il des "règles" pour déterminer quand l'un doit être utilisé plutôt qu'un autre?


je tiens à préciser qu'en "utilisant à la fois une classe abstraite et une interface" qui comprend le cas où ils représentent essentiellement la même chose ( Source et ISource ont tous les deux les mêmes membres), mais la classe ajoute la fonctionnalité Générique tandis que la interface spécifie le contrat.

vaut également la peine de noter que cette question est surtout pour les langues qui ne prennent pas en charge l'héritage multiple (comme .NET et Java).

32
demandé sur Peter Mortensen 2009-08-05 12:56:03

7 réponses

comme première règle empirique, je préfère les classes abstraites aux interfaces, basé sur les directives de conception .NET . Le raisonnement est beaucoup plus large que .NET, mais il est mieux expliqué dans le livre Framework Design Guidelines .

le principal raisonnement derrière la préférence pour les classes de base abstraites est le versioning, parce que vous pouvez toujours ajouter un nouveau membre virtuel à une classe de base abstraite sans casser les clients existants. Ce n'est pas possible avec des interfaces.

il y a des scénarios où une interface est toujours le bon choix (en particulier quand vous ne vous souciez pas de versioner), mais être conscient des avantages et des inconvénients vous permet de prendre la bonne décision.

donc comme une réponse partielle avant que je continue: avoir à la fois une interface et une classe de base n'a de sens que si vous décidez de coder contre une interface en premier lieu. Si vous autorisez une interface, vous devez coder uniquement contre cette interface, sinon vous violeriez le principe de substitution de Liskov. En d'autres termes, même si vous fournissez une classe de base qui implémente l'interface, vous ne pouvez pas laisser votre code consommer cette classe de base.

si vous décidez de coder par rapport à une classe de base, avoir une interface n'a aucun sens.

si vous décidez de coder contre une interface, avoir une classe de base qui fournit des fonctionnalités par défaut est facultatif. Il n'est pas nécessaire, mais peut accélérer les choses pour les exécutants, de sorte que vous pouvez fournir un par courtoisie.

un exemple qui me vient à l'esprit est ASP.NET MVC. Le pipeline de requête fonctionne sur IController, mais il y a une classe de base Controller que vous utilisez généralement pour implémenter behavior.

réponse Finale: Si vous utilisez une classe de base abstraite, utilisez uniquement. Si vous utilisez une interface, une classe de base est une courtoisie optionnelle pour les développeurs.


mise à Jour: je n'est plus préfèrent les classes abstraites sur les interfaces, et je n'en ai pas pour longtemps; au lieu de cela, je suis en faveur de la composition au cours de l'héritage, à l'aide de SOLIDES lignes directrices.

(bien que je puisse éditer le texte ci-dessus directement, il changerait radicalement la nature du post, et puisque quelques personnes l'ont trouvé assez précieux pour le up-vote, je préfère laisser le texte original stand, et à la place Ajouter cette remarque. La dernière partie du post est toujours significative, de sorte qu'il serait dommage de le supprimer, trop.)

34
répondu Mark Seemann 2016-11-20 14:45:29

j'ai tendance à utiliser des classes de base (abstraites ou non) pour décrire ce que signifie , tandis que j'utilise des interfaces pour décrire les" capacités d'un objet.

Un Chat est un Mammifère, mais l'un de ses capacités , c'est qu'il est Pettable.

ou, pour le dire autrement, les classes sont des noms, tandis que les interfaces se rapprochent des adjectifs.

20
répondu kyoryu 2009-08-05 10:50:46

à Partir de MSDN, Recommandations pour les Classes Abstraites vs Interfaces

  • si vous prévoyez de créer plusieurs versions de votre composant, créez une classe abstraite. Les classes abstraites offrent un moyen simple et facile de modifier vos composants. En mettant à jour la classe de base, toutes les classes héritées sont automatiquement mises à jour avec la modification. Par contre, les Interfaces ne peuvent pas être modifiées une fois créées. Si un nouvelle version de l'interface est requise, vous devez créer une toute nouvelle interface.

  • si la fonctionnalité que vous créez sera utile à travers un large éventail d'objets disparates, utilisez une interface. Les classes abstraites devraient être utilisées principalement pour les objets étroitement apparentés, tandis que les interfaces sont les mieux adaptées pour fournir des fonctionnalités communes à des classes non apparentées.

  • Si vous concevez petits morceaux concis de fonctionnalité, utiliser des interfaces. Si vous concevez de grandes unités fonctionnelles, utilisez une classe abstraite.

  • si vous voulez fournir des fonctionnalités communes mises en œuvre parmi toutes les implémentations de votre composant, utilisez une classe abstraite. Les classes abstraites vous permettent d'implémenter partiellement votre classe, tandis que les interfaces ne contiennent aucune implémentation pour les membres.

12
répondu Kishor 2012-08-21 12:09:12

si vous voulez fournir l'option de remplacer complètement votre implémentation, utilisez une interface. Cela s'applique particulièrement aux interactions entre les principaux composants, ceux-ci doivent toujours être découplés par des interfaces.

il peut aussi y avoir des raisons techniques pour préférer une interface, par exemple pour permettre la moquerie dans les essais unitaires.

en Interne, dans un composant, il peut être bon d'utiliser une classe abstraite directement accès à une hiérarchie de classe.

si vous utilisez une interface et que vous avez une hiérarchie de classes d'implémentation, alors c'est une bonne pratique d'avoir une classe abstraite qui contient les parties communes de l'implémentation. Par exemple:

interface Foo
abstract class FooBase implements Foo
class FunnyFoo extends FooBase
class SeriousFoo extends FooBase

vous pourriez aussi avoir plus de classes abstraites héritant les unes des autres pour une hiérarchie plus compliquée.

2
répondu starblue 2009-08-05 09:23:09

j'utilise toujours ces lignes directrices:

  • utiliser des interfaces pour l'héritage de types multiples (comme .NET/Java n'utilisent pas l'héritage de types multiples)
  • utiliser des classes abstraites pour une implémentation réutilisable d'un type

la règle de l'intérêt dominant stipule qu'une classe a toujours un intérêt principal et 0 ou plus d'autres (voir http://citeseer.ist.psu.edu/tarr99degrees.html ). Ceux 0 ou plus d'autres sont alors implémentés via des interfaces, car la classe implémente alors tous les types qu'elle doit implémenter (les siens, et toutes les interfaces qu'elle implémente).

dans un monde d'héritage d'implémentation multiple (par exemple C++/Eiffel), on hériterait des classes qui implémentent les interfaces. (Théoriquement. Dans la pratique, cela pourrait ne pas fonctionner aussi bien.)

2
répondu Frans Bouma 2016-11-20 14:02:40

il y a aussi quelque chose qu'on appelle le principe sec - ne vous répétez pas.

dans votre exemple de sources de données, vous dites qu'il y a un code générique qui est commun entre différentes implémentations. Pour moi, il semble que la meilleure façon de gérer cela serait d'avoir une classe abstraite avec le code générique et quelques classes concrètes l'étendant.

l'avantage est que chaque correction de bogue dans les avantages de code générique toutes les réalisations concrètes.

si vous allez interface seulement vous devrez maintenir plusieurs copies du même code qui demande des problèmes.

en ce qui concerne abstract + interface s'il n'y a pas de justification immédiate, Je ne le ferais pas. Extraire l'interface de la classe abstract est un remaniement facile, donc je le ferais seulement quand il est réellement nécessaire.

2
répondu Gregory Mostizky 2016-11-20 14:06:15

voir ci-dessous la question SE pour les lignes directrices génériques:

Interface vs Classe Abstraite générale (OO)

étui D'utilisation pratique pour interface:

  1. Implementation of Strategy_pattern : Définissez votre stratégie en tant qu'interface. Basculer la mise en œuvre de manière dynamique avec une mise en œuvre concrète de la stratégie à l'exécution temps.

  2. définit une capacité parmi plusieurs classes non apparentées.

cas D'utilisation pratique pour la classe abstraite:

  1. la mise en Œuvre de Template_method_pattern : Définir un squelette d'un algorithme. Les classes d'enfants ne peuvent pas changer la structure de l'algortihm mais ils peuvent redéfinir une partie de la mise en œuvre dans les classes pour enfants.

  2. quand vous voulez partager des variables non-statiques et non-finales parmi plusieurs classes liées avec" a une relation ".

utilisation à la fois de la classe Abstract et de l'interface:

si vous allez pour une classe abstraite, vous pouvez déplacer les méthodes abstraites à l'interface et à la classe abstraite peut simplement implémenter cette interface. Tous les cas d'utilisation de classes abstraites peuvent tomber dans cette catégorie.

0
répondu Ravindra babu 2017-05-23 12:24:34