Quand utiliser des interfaces ou des classes abstraites? Quand utiliser les deux?
Alors que certaines lignes directrices que vous devez utiliser une interface lorsque vous souhaitez définir un contrat pour une classe où l'héritage n'est pas clair (IDomesticated
) et de la succession lorsque la classe est une extension de l'autre (Cat : Mammal
, Snake : Reptile
), il ya des cas où (à mon avis) de ces lignes directrices entrer dans une zone grise.
Par exemple, dire que ma mise en œuvre a été Cat : Pet
. Pet
est une classe abstraite. Devrait-il être étendu à Cat : Mammal, IDomesticated
où Mammal
est une classe abstraite et IDomesticated
est une interface? Ou suis-je en conflit avec la KISS/YAGNI principes (même si je ne suis pas sûr qu'il y aura un Wolf
classe dans l'avenir, ce qui ne serait pas en mesure d'hériter de Pet
)?
Éloigner de la métaphorique Cat
s et Pet
s, disons que j'ai quelques classes qui représentent des sources de données entrants. Ils ont tous besoin de mettre en œuvre la même base, en quelque sorte. J'ai pu mettre en œuvre certains génériques de code dans un résumé Source
classe et hériter d'elle. J'aurais aussi pu faire un ISource
de l'interface (qui se sent plus "juste" pour moi) et de re-mettre en œuvre les génériques de code dans chaque classe (ce qui est moins intuitif). Enfin, je pourrais "avoir le gâteau et le manger" en faisant à la fois de la classe abstraite et de l'interface. Quel est le meilleur?
Ces deux cas, amener des points pour l'utilisation de seulement une classe abstraite, seulement une interface et une classe abstraite et une interface. Sont-ils tous des choix valables, ou il y a des "règles" pour quand on doit être utilisé sur un autre?
Je tiens à préciser que par "en utilisant à la fois une classe abstraite et une interface" qui inclut le cas lorsqu'ils représentent essentiellement la même chose (Source
et ISource
les deux ont les mêmes membres), mais la classe ajoute une fonctionnalité générique alors que l'interface spécifie le contrat.
Également intéressant de noter que cette question est surtout pour les langues qui ne supporte pas l'héritage multiple (tels que .NET et Java).
- Je tiens à noter que j'ai fait voir plusieurs "Interface vs les classes Abstraites" questions, mais dans cette question, je suis la plupart de tous ceux qui s'intéressent quand utiliser les deux interfaces et les classes abstraites (si c'est là une chose à faire.)
- double possible de Interface vs Classe Abstraite (général OO)
- double possible de Quand utiliser une interface au lieu d'une classe abstraite, et vice-versa?
Vous devez vous connecter pour publier un commentaire.
Première règle générale, je préfère les classes abstraites sur les interfaces, basé sur la .NET les lignes Directrices de Conception. Le raisonnement s'applique beaucoup plus large que .NET, mais c'est mieux expliqué dans le livre Cadre Des Lignes Directrices De Conception.
Les principales raisons de cette préférence pour les classes de base abstraites est la gestion des versions, parce que vous pouvez toujours ajouter un nouveau membre virtuelle d'une classe de base abstraite sans casser les clients existants. Ce n'est pas possible avec les interfaces.
Il existe des scénarios où une interface est toujours le bon choix (en particulier lorsque vous n'avez pas de soins sur le versioning), 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: Ayant à la fois une interface et une classe de base n'a de sens que si vous décidez de code par rapport à une interface en premier lieu. Si vous permettez à une interface, vous devez le code par rapport à cette interface, car sinon vous serait violer le Principe de Substitution de Liskov. En d'autres termes, même si vous fournir une base de classe qui implémente l'interface, vous ne pouvez pas laisser votre code consommer que de la classe de base.
Si vous décidez de code par rapport à une classe de base, avoir une interface n'a pas de sens.
Si vous décidez de code à l'encontre d'une interface, d'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 maîtres d'œuvre, de sorte que vous pouvez fournir un titre de courtoisie.
Un exemple qui vient à l'esprit est en ASP.NET MVC. Le pipeline de demande de travaux sur IController, mais il y a un Contrôleur de la classe de base que vous utilisez généralement pour mettre en œuvre le comportement.
Réponse finale: Si vous utilisez une classe de base abstraite, utilisez seulement. Si vous utilisez une interface, une classe de base est une option de courtoisie pour les exécutants.
Mise à jour: je 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.
(Alors que je pouvais modifier le texte ci-dessus directement, il allait changer radicalement la nature de la poste, et depuis quelques personnes ont trouvé assez précieux à voter, je préfère laisser le texte original stand, et au lieu d'ajouter cette remarque. La dernière partie du post est toujours significative, de sorte qu'il serait dommage de le supprimer, trop.)
J'ai tendance à utiliser les classes de base (abstraite ou non) pour décrire ce que quelque chose est, alors que j'ai utiliser les interfaces pour décrire la capacités d'un objet.
Un Chat est un de Mammifères, mais l'un de ses capacités est qu'il est Pettable.
Ou, pour le dire d'une autre façon, les classes sont des noms, tandis que les interfaces de la carte de plus près aux adjectifs.
À partir de MSDN, Recommandations pour les Classes Abstraites vs Interfaces
Si vous souhaitez donner la possibilité de le remplacer par votre mise en œuvre complètement, utiliser une interface. Ceci s'applique en particulier pour les interactions entre les composantes principales, celles-ci doivent toujours être découplés par des interfaces.
Il peut aussi y avoir des raisons techniques pour préférant une interface, par exemple pour activer les moqueries dans les tests unitaires.
En interne, dans un composant, il peut être bon d'utiliser une classe abstraite directement accès à une hiérarchie de classes.
Si vous utilisez une interface et une hiérarchie de la mise en œuvre de classes alors qu'il est de bonne pratique d'avoir une classe abstraite qui contiennent les parties communes de la mise en œuvre. E. g.
Vous pourriez aussi avoir plus de classes abstraites hériter les uns des autres pour une plus compliqué hiérarchie.
J'ai toujours l'utilisation de ces lignes directrices:
De l'état de la préoccupation dominante dicte qu'une classe a toujours une préoccupation principale et des 0 ou plusieurs autres (voir http://citeseer.ist.psu.edu/tarr99degrees.html). Ceux 0 ou plus autres vous est ensuite mise en œuvre par l'intermédiaire d'interfaces, comme la classe, puis met en œuvre tous les types qu'il a à mettre en œuvre (de son propre, et toutes les interfaces qu'il implémente).
Dans un monde de pluralité de mise en œuvre de l'héritage (par exemple C++/Eiffel), on pourrait hériter de classes qui implémentent les interfaces. (Dans la théorie. Dans la pratique, il peut ne pas fonctionner aussi bien.)
Il y a aussi quelque chose qui s'appelle la SEC principe - Ne pas se Répéter.
Dans votre exemple de sources de données que vous dites il y a quelques génériques de code commun entre les différentes implémentations. Il me semble que la meilleure façon de traiter ce serait d'avoir une classe abstraite avec les génériques de code dans il et certaines classes concrètes de l'étendre.
L'avantage est que chaque correction de bug dans le code général de tous les avantages des implémentations concrètes.
Si vous allez seulement l'interface que vous aurez à maintenir plusieurs copies du même code, qui est d'avoir des ennuis.
Concernant abstrait + interface si il n'y a pas de justification immédiate pour ce que je ne voudrais pas le faire. L'extraction de l'interface à partir de la classe abstraite est facile de refactoring, je voudrais donc le faire que lorsque cela est réellement nécessaire.
Consulter ci-dessous SE question pour les lignes directrices génériques:
Interface vs Classe Abstraite (général OO)
Pratique en cas d'utilisation de l'interface:
Mise en œuvre de Strategy_pattern: Définir votre stratégie comme une interface. Commutateur de la mise en œuvre de façon dynamique avec un de des implémentations concrètes de la stratégie au moment de l'exécution.
Définir un capacité entre plusieurs classes indépendantes.
Pratique en cas d'utilisation de la classe abstraite:
Mise en œuvre de Template_method_pattern: Définir un squelette d'un algorithme. Les enfants des classes ne pouvez pas changer la structure de la algortihm mais ils peuvent re-définir une partie de la mise en œuvre dans les classes enfant.
Lorsque vous souhaitez partager des non-statique et non-final variables entre plusieurs classes associées avec "a un" relation.
Utilisation des deux abstradt classe et d'interface:
Si vous allez pour une classe abstraite, vous pouvez déplacer des méthodes abstraites de 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.