Interface vs classe de Base
Quand dois-je utiliser une interface et quand dois-je utiliser une classe de base?
devrait-il toujours être une interface si Je ne veux pas réellement définir une implémentation de base des méthodes?
si j'ai une classe de chien et de chat. Pourquoi voudrais-je implémenter IPet au lieu de PetBase? Je peux comprendre avoir des interfaces pour ISheds ou IBarks (IMakesNoise?), car ceux-ci peuvent être placés sur un animal par animal, mais je ne comprends pas pour l'utiliser pour un animal de compagnie Générique.
30 réponses
Prenons votre exemple D'un chien et D'une classe de chat, et illustrons en utilisant C#:
un chien et un chat sont tous deux des animaux, plus précisément des mammifères quadrupèdes (les animaux sont beaucoup trop généraux). Supposons que vous ayez un mammifère de classe abstraite, pour les deux:
public abstract class Mammal
cette classe de base aura probablement des méthodes par défaut telles que:
- Aliments Pour Animaux
- Mate
tous des comportements qui ont plus ou moins la même implémentation entre les deux espèces. Pour définir ceci vous aurez:
public class Dog : Mammal
public class Cat : Mammal
supposons maintenant qu'il y ait d'autres mammifères, que nous verrons habituellement dans un zoo:
public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal
cela sera toujours valable car au cœur de la fonctionnalité Feed()
et Mate()
seront toujours les mêmes.
cependant, girafes, rhinocéros, et les hippopotames ne sont pas exactement des animaux dont on peut faire des animaux de compagnie. C'est là qu'une interface sera utile:
public interface IPettable
{
IList<Trick> Tricks{get; set;}
void Bathe();
void Train(Trick t);
}
la mise en œuvre pour le contrat ci-dessus ne sera pas la même entre un chat et un chien; mettre leurs mises en œuvre dans une classe abstraite à hériter sera une mauvaise idée.
les définitions de votre chien et de votre chat devraient ressembler à:
public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable
théoriquement, vous pouvez les annuler à partir d'une base supérieure classe, mais essentiellement une interface vous permet d'ajouter seulement les choses dont vous avez besoin dans une classe sans le besoin d'héritage.
par conséquent, parce que vous ne pouvez généralement hériter d'une classe abstraite (dans la plupart des langues oo statiquement dactylographiées qui est... les exceptions incluent C++) mais être capable d'implémenter des interfaces multiples, il vous permet de construire des objets dans une base strictement comme requis .
Josh Bloch a dit lui-même dans Java Effective 2d :
Préférer les interfaces sur les classes abstraites
quelques points principaux:
les classes existantes peuvent être facilement adaptées pour mettre en œuvre une nouvelle interface . Tout ce que vous avez à faire est d'ajouter les méthodes requises si elles ne le sont pas encore existe et ajoute une clause de mise en œuvre classe déclaration.
les Interfaces sont idéales pour définir les mixins . Vaguement parlant, un mixin est un type qu'une classe peut mise en œuvre en plus de ses type" pour déclarer qu'il fournit certains comportements optionnels. Exemple, Comparable est une interface mixin qui permet à une classe de déclarer que son des instances sont ordonnées en ce qui concerne: autres objets mutuellement comparables.
les Interfaces permettent la construction de type non hiérarchique cadres . Les hiérarchies de Type sont idéal pour organiser certaines choses, mais d'autres choses ne tombent pas facilement dans une hiérarchie rigide.
les Interfaces permettent des améliorations de fonctionnalité sûres et puissantes via le récapitulatif par classe idiome. Si vous utilisez classes abstraites pour définir les types, vous laisser le programmeur qui veut ajouter fonctionnalité sans alternative mais pour utiliser l'héritage.
de plus, vous pouvez combiner les vertus des interfaces et des classes abstraites par fournir un squelette abstrait classe de mise en œuvre pour aller avec chaque interface non triviale que vous exportez.
d'autre part, les interfaces sont très difficiles à développer. Si vous ajoutez une méthode à une interface il va casser tout ce qui est application.
PS.: Achetez le livre. C'est beaucoup plus détaillé.
style moderne est de définir IPet et PetBase.
l'avantage de l'interface est que d'autres codes peuvent l'utiliser sans aucun lien avec d'autres codes exécutables. Complètement "propre."Les interfaces peuvent aussi être mélangées.
mais les classes de base sont utiles pour les implémentations simples et les utilitaires communs. Ainsi, fournir une classe de base abstraite aussi bien pour gagner du temps et du code.
et les classes de base représentent deux formes différentes de relations.
héritage (classes de base) représentent une relation "is-a". E. g. un chien ou un chat "est-un" animal de compagnie. Cette relation représente toujours l'objet (unique) 151960920 "de la catégorie (en conjonction avec le " single responsibility principle " ).
Interfaces , sur l'autre la main, représentent fonctions supplémentaires d'une classe. Je dirais que c'est une relation" is", comme dans Foo
est jetable", d'où l'interface IDisposable
dans C#.
Interfaces
- définit le contrat entre 2 modules. Ne peut avoir aucune mise en œuvre.
- la plupart des langues vous permettent d'implémenter plusieurs interfaces
- modifier une interface est un changement de rupture. Toutes les implémentations doivent être recompilées/modifiées.
- Tous les membres sont publics. Les implémentations doivent implémenter tous les membres.
- aide des Interfaces Découplage. Vous pouvez utiliser des maquettes de cadres pour se moquer de tout ce qui se cache derrière une interface
- les Interfaces indiquent normalement un type de comportement
- les implémentations D'Interface sont découplées / isolées les unes des autres
classes de Base
- vous permet d'ajouter quelques par défaut implémentation que vous obtenez gratuitement par dérivation
- sauf C++, vous pouvez seulement dériver d'une classe. Même si peut de plusieurs classes, il est généralement une mauvaise idée.
- changer la classe de base est relativement facile. Les dérivés n'ont pas besoin de faire quoi que ce soit de spécial
- les classes de Base peuvent déclarer des fonctions protégées et publiques accessibles par dérivation
- classes de Base Abstraites ne peuvent pas être moqué facilement comme des interfaces Les classes de Base
- indiquent normalement une hiérarchie de type (est un)
- dérivations de classe peuvent venir à dépendre de certains comportements de base (ont une connaissance complexe de la mise en œuvre de parent). Les choses peuvent être compliquées si vous faites un changement à l'implémentation de base pour un gars et briser les autres.
en général, vous devriez préférer les interfaces aux classes abstraites. Une raison d'utiliser une classe abstraite est si vous avez une implémentation commune parmi les classes concrètes. Bien sûr, vous devez quand même déclarer une interface (IPet) et avoir une classe abstraite (PetBase) implémenter cette interface.En utilisant de petites interfaces distinctes, vous pouvez utiliser des multiples pour améliorer la flexibilité. Les Interfaces permettent un maximum de flexibilité et de portabilité des types à travers les frontières. En passant des références au-delà des frontières, toujours passer l'interface et pas le type de béton. Cela permet à la partie réceptrice de déterminer la mise en œuvre concrète et offre un maximum de flexibilité. Ceci est absolument vrai lorsque la programmation est faite de façon TDD/BDD.
le Gang de quatre a déclaré dans leur livre "parce que l'héritage expose une sous-classe aux détails de la mise en œuvre de son parent, il est souvent dit que"l'héritage casse encapsulation". Je crois que cela est vrai.
Krzysztof Cwalina dit à la page 81:
au cours des trois versions du Framework .NET, j'ai parlé de cette ligne directrice avec plusieurs développeurs de notre équipe. Beaucoup d'entre eux, y compris ceux qui étaient initialement en désaccord avec les lignes directrices, ont dit qu'ils regrettaient d'avoir envoyé certaines API comme interface. Je n'ai pas entendu parler d'un seul cas où quelqu'un a regretté d'avoir envoyé un cours.
cela dit, il y a certainement une place pour les interfaces. Comme une ligne directrice générale toujours fournir une base abstraite implémentation de classe d'une interface si pour rien d'autre comme un exemple d'une façon d'implémenter l'interface. Dans le meilleur des cas, cette classe de base économisera beaucoup de travail.
Juan,
j'aime à penser que les interfaces comme un moyen de caractériser une classe. Une classe particulière de race de chien, disons un YorkshireTerrier, peut être un descendant de la classe de chien parent, mais il est également des instruments IFurry, IStubby, et IYippieDog. Si la classe définit ce qu'est la classe, mais l'interface nous dit des choses sur elle.
l'avantage de ceci est qu'il me permet, par exemple, de rassembler tous les Iyippiedogs et les jeter dans mon océan collection. Donc maintenant je peux atteindre à travers un ensemble particulier d'objets et trouver ceux qui répondent aux critères que je regarde sans inspecter la classe trop étroitement.
je trouve que les interfaces devraient vraiment définir un sous-ensemble du comportement public d'une classe. Si elle définit tout le comportement public pour toutes les classes qui implémentent alors elle n'a généralement pas besoin d'exister. Ils ne me dites pas quelque chose d'utile.
cette pensée va à l'encontre de la l'idée que chaque classe devrait avoir une interface et vous devriez le code de l'interface. C'est bien, mais on finit avec beaucoup d'interfaces un à un pour les classes et ça rend les choses confuses. Je comprends que l'idée est qu'il ne coûte pas vraiment rien à faire et maintenant vous pouvez échanger les choses avec facilité. Cependant, je trouve que je le fais rarement. La plupart du temps, Je ne fais que modifier la classe existante en place et j'ai les mêmes problèmes que j'ai toujours fait si l'interface publique de ce la classe a besoin d'être changée, sauf que je dois la changer à deux endroits.
donc si vous pensez comme moi, vous diriez certainement que le Chat et le chien sont IPettable. C'est une caractérisation qui correspond aux deux.
l'autre partie de ceci cependant est devrait - ils avoir la même classe de base? La question Est de savoir si elles doivent être traitées globalement comme la même chose. Certes, ce sont deux animaux, mais cela correspond-il à la façon dont nous allons les utiliser ensemble.
dis que je veux rassembler toutes les classes D'animaux et les mettre dans mon conteneur D'Arche.
ou doivent-ils être des mammifères? Peut-être avons-nous besoin d'une sorte d'usine de traite animale croisée?
ont-ils même besoin d'être reliés ensemble? Est-ce suffisant de juste savoir qu'ils sont tous les deux IPettable?
je ressens souvent le désir de dériver une hiérarchie de classe entière quand j'ai vraiment besoin d'une classe. Je le fais en prévision un jour, j'en aurai peut-être besoin et d'habitude, je n'en ai jamais besoin. Même quand je le fais, je trouve que je dois faire beaucoup pour le réparer. C'est parce que la première classe que je crée n'est pas le Chien, Je ne suis pas si chanceux, c'est plutôt L'ornithorynque. Maintenant, toute ma hiérarchie de classe est basée sur le cas bizarre et j'ai beaucoup de code gaspillé.
vous pourriez également trouver à un certain point que tous les chats ne sont pas IPettable (comme celui sans poils). Vous pouvez maintenant déplacer cette Interface vers toutes les classes dérivées qui correspondent. Vous constaterez qu'un changement beaucoup moins cassant que tout d'un coup les chats ne sont plus dérivés de PettableBase.
Voici la définition de base et simple de l'interface et de la classe de base:
- classe de Base = héritage d'objet.
- Interface = transmission fonctionnelle.
cheers
je recommande d'utiliser la composition au lieu de l'héritage chaque fois que possible. Utilisez des interfaces mais utilisez des objets membres pour l'implémentation de base. De cette façon, vous pouvez définir une usine qui construit vos objets à se comporter d'une certaine façon. Si vous voulez changer le comportement, alors vous faites une nouvelle méthode d'usine (ou usine abstraite) qui crée différents types de sous-objets.
dans certains cas, vous pouvez trouver que vos objets primaires n'ont pas besoin d'interfaces du tout, si tous du comportement mutable est défini dans les objets helper.
donc au lieu de IPet ou PetBase, vous pourriez finir avec un Pet qui a un paramètre IFurBehavior. Le paramètre IFurBehavior est défini par la méthode CreateDog() de PetFactory. C'est ce paramètre qui est appelé à l'abri() la méthode.
si vous faites cela, vous constaterez que votre code est beaucoup plus flexible et que la plupart de vos objets simples traitent de comportements de base à l'échelle du système.
je recommande ce modèle même dans les langues à héritages multiples.
bien expliqué dans ce article du monde de Java
personnellement, j'ai tendance à utiliser des interfaces pour définir des interfaces - c'est-à-dire des parties de la conception du système qui précisent comment accéder à quelque chose.
il n'est pas rare que j'aie une classe qui implémente une ou plusieurs interfaces.
les classes Abstraites-je utiliser comme une base pour quelque chose d'autre.
ce qui suit est un extrait du article susmentionné JavaWorld.com article, auteur Tony Sintes, 04/20/01
Interface vs classe abstraite
le choix des interfaces et des classes abstraites n'est pas une proposition ni l'une ni l'autre. Si vous avez besoin de changer votre conception, faire une interface. Cependant, vous pouvez avoir des classes abstraites qui fournissent un certain comportement par défaut. Les classes abstraites sont d'excellents candidats à l'intérieur de cadres d'application.
les classes abstraites vous permettent de définir certains comportements; elles forcent vos sous-classes à en fournir d'autres. Par exemple, si vous avez un cadre d'application, une classe abstraite peut fournir des services par défaut tels que le traitement des événements et des messages. Ces services permettent à votre application de brancher à votre cadre d'application. Cependant, il y a certaines fonctionnalités spécifiques à l'application que seule votre application peut exécuter. Cette fonctionnalité pourrait inclure: les tâches de démarrage et d'arrêt, qui sont souvent dépendantes de l'application. Ainsi, au lieu d'essayer de définir ce comportement lui-même, la classe de base abstraite peut déclarer des méthodes d'arrêt et de démarrage abstraits. La classe de base sait qu'il a besoin de ces méthodes, mais une classe abstraite permet à votre classe d'admettre qu'il ne sait pas comment effectuer ces actions; il sait seulement qu'il doit entreprendre les actions. Quand il est temps de le lancer, la classe abstraite peut appeler la méthode de démarrage. Quand la classe de base appelle ça méthode, Java appelle la méthode définie par la classe enfant.
Beaucoup de développeurs oublier qu'une classe qui définit une méthode abstraite peut appeler cette méthode. Les classes abstraites sont un excellent moyen de créer des hiérarchies d'héritage planifiées. Ils sont également un bon choix pour les classes non-Leaf dans les hiérarchies de classe.
Classe vs. interface
certains disent que vous devriez définir toutes les classes en termes d'interfaces, mais je pense recommandation semble un peu extrême. J'utilise des interfaces quand je vois que quelque chose dans ma conception va changer fréquemment.
par exemple, le pattern de stratégie vous permet d'échanger de nouveaux algorithmes et processus dans votre programme sans modifier les objets qui les utilisent. Un lecteur multimédia peut savoir lire des CDs, des MP3s et des fichiers wav. Bien sûr, vous ne voulez pas de hardcode ces algorithmes de lecture dans le lecteur; cela rendra difficile d'ajouter un nouveau format comme AVI. De plus, votre code sera jonché d'énoncés de cas inutiles. Et pour ajouter l'insulte à la blessure, vous aurez besoin de mettre à jour ces déclarations chaque fois que vous ajoutez un nouvel algorithme. Dans l'ensemble, ce n'est pas une méthode orientée objet de la programmation.
avec le pattern de stratégie, vous pouvez simplement encapsuler l'algorithme derrière un objet. Si vous faites cela, vous pouvez fournir des nouveaux médias plugins à tout moment. Appelons la classe plug-in MediaStrategy. Cet objet aurait un méthode: playStream (Stream s). Donc pour ajouter un nouvel algorithme, nous élargissons simplement notre classe d'algorithme. Maintenant, lorsque le programme rencontre le type nouveau média, il délègue simplement la lecture du flux à notre stratégie médiatique. Bien sûr, vous aurez besoin d'un peu de plomberie pour instancier correctement les stratégies d'algorithme dont vous aurez besoin.
C'est un excellent endroit pour utiliser une interface. Nous avons utilisé le schéma stratégique, qui indique clairement une place dans la conception qui va changer. Ainsi, vous devez définir la stratégie comme une interface. Vous devez généralement préférer les interfaces à l'héritage lorsque vous voulez qu'un objet ait un certain type; dans ce cas, MediaStrategy. Compter sur l'héritage pour l'identité de type est dangereux; il vous enferme dans une hiérarchie d'héritage particulière. Java ne permet pas l'héritage multiple, donc vous ne pouvez pas étendre quelque chose qui vous donne une implémentation utile ou plus d'identité de type.
également garder à l'esprit de ne pas être balayé dans OO ( voir blog ) et toujours modéliser des objets basés sur le comportement requis, si vous conceviez une application où le seul comportement dont vous avez besoin était un nom générique et une espèce pour un animal, alors vous n'auriez besoin que D'un Animal de classe avec une propriété pour le nom, au lieu de millions de classes pour chaque animal possible dans le monde.
j'ai un accidenté de la règle-de-pouce
fonctionnalité: susceptible d'être différente dans toutes les parties: Interface.
données, et la fonctionnalité, les parties seront pour la plupart les mêmes, les parties différentes: classe abstraite.
données, et fonctionnalité, fonctionnant effectivement, si étendue seulement avec de légères modifications: classe ordinaire (béton)
données et Fonctionnalité, Aucun changement prévu: classe ordinaire (béton) avec modificateur final.
des Données, et peut-être la fonctionnalité: lecture seule: enum membres.
c'est très rugueux et prêt et pas du tout strictement défini, mais il ya un spectre d'interfaces où tout est destiné à être changé en enum où tout est fixé un peu comme un fichier en lecture seule.
doivent être petites. Vraiment petit. Si vous décomposez vraiment vos objets, alors vos interfaces ne contiendront probablement que quelques méthodes et propriétés très spécifiques.
les classes abstraites sont des raccourcis. Y a-t-il des choses que tous les dérivés de PetBase partagent que vous pouvez coder une fois et être fait avec? Si oui, alors c'est l'heure d'un cours d'abstrait.
Les classes abstraitessont également limitatives. Alors qu'ils vous donnent une grande raccourci produire des objets enfants, n'importe quel objet donné ne peut mettre en œuvre qu'une classe abstraite. Souvent, je trouve que c'est une limitation des classes abstraites, et c'est pourquoi j'utilise beaucoup d'interfaces.
les classes abstraites peuvent contenir plusieurs interfaces. Votre classe de résumé PetBase peut implémenter IPet (les animaux ont des propriétaires) et IDigestion (les animaux mangent, ou du moins ils devraient). Cependant, PetBase ne mettra probablement pas en œuvre IMammal, car tous les animaux de compagnie ne sont pas des mammifères et tous les mammifères ne sont pas des animaux de compagnie. Vous pouvez ajouter une MammalPetBase qui étend PetBase et Ajouter IMammal. FishBase pourrait avoir PetBase et Ajouter IFish. IFish aurait ISwim et IUnderwaterBreather comme interfaces.
Oui, mon exemple est beaucoup trop compliqué pour l'exemple simple, mais c'est une partie de la grande chose sur la façon dont les interfaces et les classes abstraites fonctionnent ensemble.
le cas des Classes de Base sur Interfaces a été bien expliqué dans les directives de codage du sous-domaine .NET:
Classes de Base vs. Interfaces Un type d'interface est une partielle description d'une valeur, potentiellement soutenu par de nombreux types d'objets. Utiliser classes de base au lieu d'interfaces chaque fois que possible. À partir d'une gestion des versions perspective, les classes sont plus flexibles que des interfaces. Avec un cours, vous pouvez navire de la Version 1.0 et puis en Version 2.0 ajouter une nouvelle méthode à la classe. Tant que la méthode n'est pas abstrait, toutes les classes dérivées existantes continuent pour la fonction inchangée.
parce que les interfaces ne prennent pas en charge la mise en œuvre de l'héritage, de la le modèle qui s'applique aux catégories ne s'applique pas aux interfaces. L'ajout d'un méthode d'une interface est équivalent ajouter une méthode abstraite à une base Classe; toute classe qui met en œuvre interface cassera parce que la classe ne pas mettre en œuvre la nouvelle méthode. Les Interfaces sont appropriées dans situations suivantes:
- Plusieurs classes indépendantes veulent soutenir le protocole.
- ces classes ont déjà établi des classes de base (pour exemple, certaines sont des commandes D'interface utilisateur (UI) , et certains sont des services web XML).
L'agrégation- n'est ni appropriée ni réalisable. Dans tous les autres situation, l'héritage de classe est un meilleur modèle.
une différence importante est que vous ne pouvez hériter un classe de base, mais vous pouvez mettre en œuvre beaucoup interfaces. Si vous souhaitez uniquement utiliser une classe de base si vous êtes absolument certain que vous n'aurez pas besoin d'hériter d'une autre classe de base. En outre, si vous trouvez que votre interface est de plus en plus large, alors vous devriez commencer à chercher à le décomposer en quelques morceaux logiques qui définissent la fonctionnalité indépendante, puisqu'il n'y a aucune règle que votre classe ne peut pas les implémenter toutes (ou que vous pouvez définir une interface différente qui les hérite toutes pour les grouper).
quand j'ai commencé à apprendre la programmation orientée objet, j'ai fait l'erreur facile et probablement commune d'utiliser l'héritage pour partager un comportement commun - même si ce comportement n'était pas essentiel à la nature de l'objet.
pour construire davantage sur un exemple beaucoup utilisé dans cette question particulière, il y a lots de choses qui sont petables - petites amies, voitures, couvertures floues... - donc j'aurais pu avoir une classe agréable qui a fourni ce comportement commun, et diverses classes en héritant.
cependant, être agréable ne fait pas partie de la nature de l'un de ces objets. Il existe des concepts beaucoup plus importants que sont essentiel à leur nature - la petite amie est une personne, la voiture est un véhicule terrestre, le chat est un mammifère...
Les comportementsdoivent être attribués en premier aux interfaces (y compris l'interface par défaut de la classe), et promus à une classe de base seulement si ils sont communs à un grand groupe de classes qui sont des sous-ensembles d'une grande classe dans le même sens que "chat" et "personne" sont des sous-ensembles de "mammifère".
le piège est, après que vous comprenez le design orienté objet suffisamment mieux que je ne l'ai fait au début, vous le ferez normalement automatiquement sans même y penser. Donc la simple vérité de la déclaration "code à une interface, pas une classe abstraite" devient si évidente que vous avez du mal à croire n'importe qui prendrait la peine de le dire - et de commencer à essayer de lire d'autres significations.
une autre chose que j'ajouterais est que si une classe est purement Abstrait - Sans membres non abstraits, Non hérités ou méthodes exposés à l'enfant, parent, ou client-alors pourquoi est-ce une classe? Il pourrait être remplacé, dans certains cas par une interface et dans d'autres cas par Null.
Source : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use /
C# est une langue merveilleuse qui a mûri et évolué au cours des 14 dernières années. C'est génial pour nous développeurs parce qu'une langue mature nous fournit une pléthore de fonctionnalités langagières qui sont à notre disposition.
cependant, avec beaucoup de pouvoir devient beaucoup de responsabilité. Certains de ces les fonctionnalités peuvent être mal utilisées, ou parfois il est difficile de comprendre pourquoi vous choisiriez d'utiliser une fonctionnalité plutôt qu'une autre. Au fil des ans, une fonctionnalité que j'ai vu de nombreux développeurs du mal avec quand choisir d'utiliser une interface, ou de choisir d'utiliser une classe abstraite. Les deux ont là des avantages et des inconvénients et le moment et l'endroit corrects pour utiliser chacun. Mais comment nous y prendre???
prévoient tous deux la réutilisation de fonctionnalités communes entre types. La différence la plus évidente tout de suite est que les interfaces ne fournissent aucune implémentation pour leur fonctionnalité alors que les classes abstraites vous permettent d'implémenter un comportement "de base" ou "par défaut" et ont ensuite la capacité de "surpasser" ce comportement par défaut avec les types dérivés des classes si nécessaire.
tout cela est très bien et permet une grande réutilisation du code et adhère au principe sec (Ne vous répétez pas) du développement logiciel. Les classes abstraites sont excellentes à utiliser lorsque vous avez un "est un" relation.
Par exemple: Un golden retriever "est" un type de chien. C'est donc un caniche. Ils aboient tous les deux, comme tous les chiens. Cependant, vous pourriez vouloir déclarer que le parc caniche est significativement différent de l'écorce de chien "par défaut". Par conséquent, il pourrait être logique pour vous de mettre en œuvre quelque chose comme suit:
public abstract class Dog
{
public virtual void Bark()
{
Console.WriteLine("Base Class implementation of Bark");
}
}
public class GoldenRetriever : Dog
{
// the Bark method is inherited from the Dog class
}
public class Poodle : Dog
{
// here we are overriding the base functionality of Bark with our new implementation
// specific to the Poodle class
public override void Bark()
{
Console.WriteLine("Poodle's implementation of Bark");
}
}
// Add a list of dogs to a collection and call the bark method.
void Main()
{
var poodle = new Poodle();
var goldenRetriever = new GoldenRetriever();
var dogs = new List<Dog>();
dogs.Add(poodle);
dogs.Add(goldenRetriever);
foreach (var dog in dogs)
{
dog.Bark();
}
}
// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark
//
comme vous pouvez le voir, ce serait un excellent moyen de garder votre code au sec et de permettre à l'implémentation de classe de base d'être appelée quand n'importe quel des types peuvent juste compter sur L'écorce par défaut au lieu d'une implémentation de cas spécial. Les classes comme GoldenRetriever, Boxer, Lab pourraient toutes hériter de L'écorce" par défaut " (classe de basse) gratuitement juste parce qu'elles mettent en œuvre la classe abstraite de chien.
mais je suis sûr que vous le saviez déjà.
Vous êtes ici parce que vous voulez comprendre pourquoi vous pourriez vouloir choisir une interface par une classe abstraite ou vice versa. Eh bien, une raison que vous voulez peut-être choisir une interface au-dessus d'une classe abstraite est quand vous n'avez pas ou que vous voulez empêcher une implémentation par défaut. C'est généralement parce que les types qui mettent en œuvre l'interface non liée dans une relation "est un". En fait, ils n'ont pas à être liés du tout sauf pour le fait que chaque type "est capable" ou a "l'abilité" de faire quelque chose ou avoir quelque chose.
qu'est-ce que ça veut dire? Ainsi, par exemple: Un homme n'est pas un canard...et un canard n'est pas un être humain. Assez évident. Cependant, un canard et un humain ont "la capacité" de nager (étant donné que l'humain a passé ses leçons de natation en première année :) ). En outre, puisqu'un canard n'est pas un humain ou vice versa, ce n'est pas une relation "est une" réalisation, mais plutôt une relation "est capable" et nous pouvons utiliser une interface pour illustrer que:
// Create ISwimable interface
public interface ISwimable
{
public void Swim();
}
// Have Human implement ISwimable Interface
public class Human : ISwimable
public void Swim()
{
//Human's implementation of Swim
Console.WriteLine("I'm a human swimming!");
}
// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
public void Swim()
{
// Duck's implementation of Swim
Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
}
}
//Now they can both be used in places where you just need an object that has the ability "to swim"
public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
somethingThatCanSwim.Swim();
}
public void Main()
{
var human = new Human();
var duck = new Duck();
var listOfThingsThatCanSwim = new List<ISwimable>();
listOfThingsThatCanSwim.Add(duck);
listOfThingsThatCanSwim.Add(human);
foreach (var something in listOfThingsThatCanSwim)
{
ShowHowYouSwim(something);
}
}
// So at runtime the correct implementation of something.Swim() will be called
// Output:
// Quack! Quack! I'm a Duck swimming!
// I'm a human swimming!
Utilisation des interfaces comme le code ci-dessus vous permettra de passer un objet dans une méthode qui "est capable de" faire quelque chose. Le code ne nécessite pas de soins comment il le fait...tout ce qu'il sait c'est qu'il peut appeler la méthode Swim sur cet objet et que cet objet saura quel comportement prendre à l'exécution en fonction de son type.
encore une fois, cela aide votre code à rester sec afin que vous n'ayez pas à écrire plusieurs méthodes qui appellent l'objet pour préformer la même fonction de base (ShowHowHumanSwims(humain), ShowHowDuckSwims(canard), etc.)
L'utilisation d'une interface permet ici aux méthodes d'appel de ne pas avoir à s'inquiéter sur le type qui est qui ou comment le comportement est mis en œuvre. Il sait juste que compte tenu de l'interface, chaque objet devra avoir mis en œuvre la méthode Swim afin qu'il soit sûr de l'appeler dans son propre code et permettre le comportement de la méthode Swim être manipulé dans sa propre classe.
résumé:
donc ma règle de base principale est d'utiliser une classe abstraite quand vous voulez implémenter une fonctionnalité "par défaut" pour une hiérarchie de classe ou / et les classes ou les types que vous êtes travailler avec partager une "un" de la relation (ex. caniche "est" un type de chien).
d'autre part, utilisez une interface lorsque vous n'avez pas de relation" est une", mais que vous avez des types qui partagent" la capacité " de faire quelque chose ou d'avoir quelque chose (ex. Canard "n'est pas" un homme. Cependant, le canard et l'humain partagent "la capacité" de nager).
une Autre différence à noter entre les classes abstraites et les interfaces est qu'une classe peut implémenter plusieurs interfaces, mais une classe peut seulement hériter d'UNE classe abstraite (ou une catégorie). Oui, vous pouvez intégrer des classes et avoir une hiérarchie d'héritage (ce que font et devraient faire de nombreux programmes) mais vous ne pouvez pas hériter deux classes dans une définition de classe dérivée (cette règle s'applique à C#. Dans d'autres langues, vous êtes en mesure de le faire, généralement seulement en raison du manque d'interfaces dans ces langues).
se rappeler également lors de l'utilisation des interfaces d'adhérer au principe de ségrégation D'Interface (ISP). Le FSI affirme qu'aucun client ne devrait être forcé de dépendre de méthodes qu'il n'utilise pas. Pour cette raison, les interfaces doivent être axées sur des tâches spécifiques et sont généralement très petites (p. ex. IDisposable, IComparable).
un autre conseil est que si vous développez de petits bits concis de fonctionnalité, utilisez des interfaces. Si vous concevez de grandes unités fonctionnelles, utilisez une classe abstraite.
J'espère que cela éclaircira les choses pour certaines personnes!
également si vous pouvez penser à de meilleurs exemples ou vous voulez souligner quelque chose, s'il vous plaît le faire dans les commentaires ci-dessous!
les commentaires précédents sur l'utilisation des classes abstraites pour la mise en œuvre commune sont certainement pertinents. Un avantage que je n'ai pas encore vu mentionné est que l'utilisation d'interfaces rend beaucoup plus facile de mettre en œuvre des objets simulés aux fins de l'essai de l'unité. Définir IPet et PetBase comme Jason Cohen l'a décrit vous permet de vous moquer facilement de différentes conditions de données, sans la tête d'une base de données physique (jusqu'à ce que vous décidez qu'il est temps de tester la chose réelle).
N'utilisez pas de classe de base à moins que vous ne sachiez ce que cela signifie Et que cela s'applique dans ce cas. S'il s'applique, utilisez-le, sinon, utilisez des interfaces. Mais notez la réponse à propos des petites interfaces.
L'héritage public est surutilisé dans OOD et exprime beaucoup plus que la plupart des développeurs réalisent ou sont prêts à vivre à la hauteur. Voir le Liskov principe de substituabilité
en bref, si A "est a" B alors a ne nécessite pas plus de B et offre pas moins de B, pour chaque méthode qu'il expose.
sur le plan conceptuel, une Interface est utilisée pour définir formellement et semi-formellement un ensemble de méthodes qu'un objet fournira. Officiellement signifie un ensemble de noms de méthode et de signatures, semi-officiellement signifie la documentation lisible par l'homme associée à ces méthodes. Les Interfaces ne sont que des descriptions D'une API (après tout, API signifie Application Programmer Interface ), ils ne peuvent pas contenir n'importe quelle implémentation, et il n'est pas possible d'utiliser ou exécuter une Interface. Ils ne rendre explicite le contrat de la façon dont vous devez interagir avec un objet.
Les Classesfournissent une implémentation, elles peuvent déclarer qu'elles implémentent zéro, une ou plusieurs Interfaces. Si une classe est destinée à être héritée, la convention est de préfixer le nom de la classe par "Base".
il y a une distinction entre une classe de base et une classe de base abstraite (ABC). L'interface et la mise en œuvre de l'ABCs sont combinées. Résumé en dehors des moyens de programmation informatique "résumé", C'est-à-dire "Abstract == Interface". Une classe de Base abstraite peut alors décrire à la fois une interface, ainsi qu'une implémentation vide, partielle ou complète qui est destinée à être héritée.
les Opinions sur le moment d'utiliser les Interfaces versus les Classes de base abstraites versus les Classes justes vont varier énormément en fonction de ce que vous développez, et de la langue dans laquelle vous développez. Les Interfaces ne sont souvent associées qu'à des langages statiquement typés tels que Java ou C#, mais les langages dactylographiés dynamiquement peuvent aussi avoir des Interfaces et des Classes de base abstraites. En Python par exemple, la distinction est faite clairement entre une classe, qui déclare qu'elle implémente une Interface, et un objet, qui est une instance d'une classe, et est dit à fournir cette Interface. Il est possible dans un langage dynamique que deux objets qui sont deux instances de la même classe, puissent déclarer qu'ils fournissent complètement différentes interfaces . En Python, cela n'est possible que pour les attributs d'objet, alors que les méthodes sont partagées entre tous les objets d'une classe. Cependant, dans Ruby, les objets peuvent avoir des méthodes par instance, de sorte qu'il est possible que l'Interface entre deux objets de la même classe puisse varier autant que le programmeur le désire (cependant, Ruby n'a aucune façon explicite de déclarer les Interfaces).
dans les langages dynamiques, L'Interface avec un objet est souvent implicite. supposé, soit en introspectant un objet et en lui demandant quelles méthodes il fournit (regarder avant de sauter) ou de préférence en essayant simplement d'utiliser l'Interface désirée sur un objet et en saisissant des exceptions si l'objet ne fournit pas cette Interface (plus facile de demander pardon que la Permission). Cela peut conduire à des "faux positifs" où deux Interfaces ont le même nom de méthode mais sont sémantiquement différentes, cependant le compromis est que votre code est plus flexible puisque vous n'avez pas besoin de sur spécifier d'avance pour anticiper toutes les utilisations possibles de votre code.
une autre option à garder à l'esprit est d'utiliser la relation" has-a", alias" est mis en œuvre en termes de "ou" composition."Parfois, c'est une façon plus propre et plus souple de structurer les choses que d'utiliser l'héritage "est-a".
il n'est peut-être pas aussi logique de dire que le chien et le chat" ont "un animal de compagnie, mais cela évite les pièges de l'héritage multiple commun:
public class Pet
{
void Bathe();
void Train(Trick t);
}
public class Dog
{
private Pet pet;
public void Bathe() { pet.Bathe(); }
public void Train(Trick t) { pet.Train(t); }
}
public class Cat
{
private Pet pet;
public void Bathe() { pet.Bathe(); }
public void Train(Trick t) { pet.Train(t); }
}
Oui, cet exemple montre qu'il y a beaucoup de duplication de code et le manque d'élégance impliqué dans faire les choses de cette façon. Mais il faut également comprendre que cela aide à garder le chien et le chat découplé de la classe de L'animal de compagnie (dans ce chien et le chat n'ont pas accès aux membres privés de L'animal de compagnie), et il laisse de la place pour le chien et le chat d'hériter de quelque chose d'autre--peut-être la classe de mammifère.
La Compositionest préférable lorsqu'aucun accès privé n'est requis et qu'il n'est pas nécessaire de faire référence au chien et au chat en utilisant des références/pointeurs génériques pour animaux de compagnie. Les Interfaces vous donnent cette capacité de référence générique et peuvent aider à réduire la verbosité de votre code, mais elles peuvent aussi brouiller les choses lorsqu'elles sont mal organisées. L'héritage est utile quand vous avez besoin de l'accès de membre privé, et en l'utilisant, vous vous engagez à coupler fortement vos cours de chien et de chat à votre classe D'animal de compagnie, ce qui est un coût élevé à payer.
entre héritage, composition, et interfaces il n'y a pas une seule voie qui est toujours juste, et il aide à considérer comment les trois options peuvent être utilisées en harmonie. Des trois, l'héritage est généralement l'option qui doit être utilisé le moins souvent.
Préférer les interfaces sur les classes abstraites
justification, les principaux points à considérer [deux déjà mentionnés ici] sont:
- les Interfaces sont plus flexibles, car une classe peut implémenter plusieurs interface. Depuis Java n'a pas d'héritage multiple, en utilisant les classes abstraites empêchent vos utilisateurs d'utiliser toute autre classe hiérarchie. en général, préfèrent les interfaces lorsqu'il n'y a pas défaut les implémentations ou de l'état. collections Java offrent de bons exemples de this (Map, Set, etc.).
- les classes Abstraites ont l'avantage de permettre une meilleure compatibilité. Une fois que les clients utilisent une interface, vous ne pouvez pas la changer; si ils utilisent une classe abstraite, vous pouvez toujours ajouter un comportement sans briser le code existant. si la compatibilité est un problème, envisager d'utiliser les classes abstraites.
- même si vous avez implémentations par défaut ou état interne,
envisager d'offrir une interface et une implémentation abstraite de it .
Cela aidera les clients, mais leur permettra tout de même une plus grande liberté si
touche souhaitée [1].
Bien sûr, le sujet a été longuement discuté. ailleurs [2,3].
[1] Il ajoute plus de code, Bien sûr, mais si la brièveté est votre principale préoccupation, vous auriez probablement dû éviter Java en premier lieu!
[2] Joshua Bloch, Effective Java, points 16-18.
[3] http://www.codeproject.com/KB/ar ...
cela dépend de vos besoins. Si L'IPet est assez simple, je préférerais l'implémenter. Sinon, si PetBase implémente une tonne de fonctionnalités que vous ne voulez pas dupliquer, alors ayez-le.
l'inconvénient de la mise en œuvre d'une classe de base est l'exigence relative aux méthodes existantes override
(ou new
). Cela en fait des méthodes virtuelles, ce qui signifie que vous devez faire attention à la façon dont vous utilisez l'instance de l'objet.
enfin, la un seul héritage de .NET me tue. Un exemple naïf: disons que vous prenez le contrôle d'un utilisateur, donc vous héritez de UserControl
. Mais maintenant, vous êtes exclu d'hériter aussi de PetBase
. Cela vous oblige à vous Réorganiser, par exemple à faire un membre de classe PetBase
, à la place.
@Joel: certaines langues (par exemple, C++) permettent l'héritage multiple.
d'habitude, je n'exécute pas non plus jusqu'à ce que j'en ai besoin. Je préfère les interfaces aux classes abstraites car cela donne un peu plus de flexibilité. S'il y a un comportement commun dans certaines classes héritées, je le déplace et je crée une classe de base abstraite. Je ne vois pas le besoin pour les deux, car ils servent essentiellement le même but, et avoir les deux est une mauvaise odeur de code (imho) que la solution a été sur-Conçu.
en ce qui concerne C#, dans certains sens les interfaces et les classes abstraites peuvent être interchangeables. Toutefois, les différences sont les suivantes: i) les interfaces ne peuvent pas implémenter le code; ii) pour cette raison, les interfaces ne peuvent pas appeler plus haut la pile vers la sous-classe; et iii) seule la classe abstraite peut être héritée sur une classe, alors que des interfaces multiples peuvent être implémentées sur une classe.
j'ai trouvé qu'un modèle D'Interface > Abstract > œuvres concrètes dans le cas d'utilisation suivant:
1. You have a general interface (eg IPet)
2. You have a implementation that is less general (eg Mammal)
3. You have many concrete members (eg Cat, Dog, Ape)
la classe abstraite définit les attributs partagés par défaut des classes concrètes, tout en renforçant l'interface. Par exemple:
public interface IPet{
public boolean hasHair();
public boolean walksUprights();
public boolean hasNipples();
}
maintenant, puisque tous les mammifères ont des poils et des mamelons (AFAIK, Je ne suis pas zoologiste), nous pouvons rouler ceci dans la classe de base abstraite
public abstract class Mammal() implements IPet{
@override
public walksUpright(){
throw new NotSupportedException("Walks Upright not implemented");
}
@override
public hasNipples(){return true}
@override
public hasHair(){return true}
et ensuite le béton les classes ne font que définir qu'elles marchent debout.
public class Ape extends Mammal(){
@override
public walksUpright(return true)
}
public class Catextends Mammal(){
@override
public walksUpright(return false)
}
cette conception est agréable quand il ya beaucoup de classes de béton, et vous ne voulez pas maintenir boilerplate juste pour programmer à une interface. Si de nouvelles méthodes étaient ajoutées à l'interface, cela briserait toutes les classes résultantes, donc vous obtenez toujours les avantages de l'approche d'interface.
dans ce cas, l'abstrait pourrait tout aussi bien être concret; toutefois, le la désignation abstraite aide à souligner que ce modèle est utilisé.
Un héritier d'une classe de base doit avoir un "est" une relation. L'Interface représente une relation" implements a". Donc n'utilisez une classe de base que lorsque vos héritiers maintiendront une relation.
utilise des Interfaces pour appliquer un contrat à travers des familles de classes non apparentées. Par exemple, vous pouvez avoir des méthodes d'accès communes pour les classes qui représentent des collections, mais contiennent des données radicalement différentes, c'est-à-dire qu'une classe peut représenter un ensemble de résultats d'une requête, tandis que l'autre peut représenter les images dans une galerie. Aussi, vous pouvez implémenter plusieurs interfaces, vous permettant ainsi de mélange (et signifier) les capacités de la classe.
Use héritage les classes ont une relation commune et ont donc une signature structurale et comportementale similaire, c'est-à-dire que la voiture, la moto, le camion et le 4x4 sont tous des types de véhicules routiers qui peuvent contenir un certain nombre de roues, une vitesse de pointe