Quand dois-je utiliser une interface et quand dois-je utiliser une classe de base?
Doit-il toujours s'agir d'une interface si je ne souhaite pas réellement définir une implémentation de base des méthodes?
Si j'ai un cours pour chiens et chats. Pourquoi voudrais-je implémenter IPet au lieu de PetBase? Je peux comprendre avoir des interfaces pour ISheds ou IBarks (IMakesNoise?), Car celles-ci peuvent être placées animal par animal, mais je ne comprends pas lesquelles utiliser pour un animal générique.
I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.
. Je ne peux pas comprendre ce que l'on entend dans l'extrait. Nous pouvons créer quelques types de base et créer un type dérivé pour chacun d'entre eux, afin qu'un développeur puisse choisir un type de base. Quelqu'un pourrait-il expliquer, s'il vous plaît, ce qui me manque? Je pense que cela peut faire partie de cette question. Ou devrais-je en poster un autre sur l'extrait spécifique?Réponses:
Prenons votre exemple de classe Chien et Chat et illustrons l'utilisation de C #:
Un chien et un chat sont des animaux, en particulier des mammifères quadrupèdes (les animaux sont trop généraux). Supposons que vous ayez une classe abstraite Mammifère, pour les deux:
Cette classe de base aura probablement des méthodes par défaut telles que:
Tous sont des comportements qui ont plus ou moins la même implémentation entre les deux espèces. Pour définir cela, vous aurez:
Supposons maintenant qu'il existe d'autres mammifères, que nous verrons généralement dans un zoo:
Ce sera toujours valable car au cœur de la fonctionnalité
Feed()
etMate()
sera toujours le même.Cependant, les girafes, les rhinocéros et les hippopotames ne sont pas exactement des animaux dont vous pouvez faire des animaux de compagnie. C'est là qu'une interface sera utile:
La mise en œuvre du contrat ci-dessus ne sera pas la même entre un chat et un chien; mettre leurs implémentations dans une classe abstraite pour hériter sera une mauvaise idée.
Vos définitions de chien et de chat devraient maintenant ressembler à:
Théoriquement, vous pouvez les remplacer par une classe de base supérieure, mais essentiellement une interface vous permet d'ajouter uniquement les éléments dont vous avez besoin dans une classe sans avoir besoin d'héritage.
Par conséquent, comme vous ne pouvez généralement hériter que d'une seule classe abstraite (dans la plupart des langages OO à typage statique c'est-à-dire ... les exceptions incluent C ++) mais être capable d'implémenter plusieurs interfaces, cela vous permet de construire des objets de manière strictement requise .
la source
Eh bien, Josh Bloch s'est dit dans Effective Java 2d :
Préférer les interfaces aux classes abstraites
Quelques points principaux:
En revanche, les interfaces sont très difficiles à faire évoluer. Si vous ajoutez une méthode à une interface, elle interrompra toutes ses implémentations.
PS .: Achetez le livre. C'est beaucoup plus détaillé.
la source
Les interfaces et les classes de base représentent deux formes de relations différentes.
L'héritage (classes de base) représente une relation "est-un". Par exemple, un chien ou un chat "est-un" animal de compagnie. Cette relation représente toujours le (seul) objectif de la classe (en liaison avec le « seul principe de responsabilité » ).
Les interfaces , en revanche, représentent des fonctionnalités supplémentaires d'une classe. Je l'appellerais une relation "est", comme dans "
Foo
est jetable", d'où l'IDisposable
interface en C #.la source
Le style moderne consiste à définir IPet et PetBase.
L'avantage de l'interface est qu'un autre code peut l'utiliser sans aucun lien avec un autre code exécutable. Complètement "propre". Les interfaces peuvent également être mélangées.
Mais les classes de base sont utiles pour les implémentations simples et les utilitaires courants. Fournissez donc également une classe de base abstraite pour économiser du temps et du code.
la source
Interfaces
Classes de base
la source
En général, vous devriez privilégier 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 toujours 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 encore la flexibilité. Les interfaces permettent un maximum de flexibilité et de portabilité des types à travers les frontières. Lorsque vous passez des références au-delà des limites, passez toujours l'interface et non le type concret. Cela permet à l'extrémité réceptrice de déterminer la mise en œuvre concrète et offre une flexibilité maximale. Cela est absolument vrai lors de la programmation d'une manière TDD / BDD.
Le Gang of Four a déclaré dans son livre "Parce que l'héritage expose une sous-classe aux détails de la mise en œuvre de ses parents, on dit souvent que" l'héritage rompt l'encapsulation ". Je pense que c'est vrai.
la source
C'est assez spécifique à .NET, mais le livre Framework Design Guidelines soutient qu'en général les classes donnent plus de flexibilité dans un framework en évolution. Une fois qu'une interface est livrée, vous n'avez pas la possibilité de la changer sans casser le code qui a utilisé cette interface. Cependant, avec une classe, vous pouvez la modifier et ne pas casser le code qui y est lié. Tant que vous apportez les bonnes modifications, notamment l'ajout de nouvelles fonctionnalités, vous pourrez étendre et faire évoluer votre code.
Krzysztof Cwalina dit à la page 81:
Cela étant dit, il y a certainement une place pour les interfaces. En règle générale, fournissez toujours une implémentation de classe de base abstraite d'une interface, si ce n'est pour rien d'autre comme exemple de mise en œuvre de l'interface. Dans le meilleur des cas, cette classe de base économisera beaucoup de travail.
la source
Juan,
J'aime penser aux interfaces comme un moyen de caractériser une classe. Une classe de race de chien particulière, par exemple un YorkshireTerrier, peut être une descendante de la classe de chien parent, mais elle met également en œuvre IFurry, IStubby et IYippieDog. La classe définit donc ce qu'est la classe, mais l'interface nous en dit long.
L'avantage de cela est qu'il me permet, par exemple, de rassembler tous les IYippieDog's et de les jeter dans ma collection Ocean. Alors maintenant, je peux atteindre un ensemble particulier d'objets et trouver ceux qui répondent aux critères que je regarde sans inspecter la classe de trop près.
Je trouve que les interfaces devraient vraiment définir un sous-ensemble du comportement public d'une classe. S'il définit tout le comportement public pour toutes les classes qui l'implémentent, il n'a généralement pas besoin d'exister. Ils ne me disent rien d'utile.
Cette pensée va cependant à l'encontre de l'idée que chaque classe devrait avoir une interface et que vous devriez coder pour l'interface. C'est bien, mais vous vous retrouvez avec beaucoup d'interfaces individuelles pour les classes et cela rend les choses confuses. Je comprends que l'idée est que cela ne coûte vraiment rien et que vous pouvez maintenant échanger des objets facilement. Cependant, je trouve que je fais rarement cela. La plupart du temps, je modifie simplement la classe existante en place et j'ai exactement les mêmes problèmes que je faisais toujours si l'interface publique de cette classe doit changer, sauf que je dois maintenant 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 leur correspond.
L'autre élément de ceci est cependant devrait-il avoir la même classe de base? La question est de savoir s'ils doivent être traités globalement comme la même chose. Certes, ils sont tous les deux des 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 Ark.
Ou doivent-ils être des mammifères? Peut-être avons-nous besoin d'une sorte d'usine de traite d'animaux croisés?
Doivent-ils même être liés entre eux? Suffit-il simplement de savoir qu'ils sont tous les deux IPables?
Je ressens souvent le désir de dériver une hiérarchie de classe entière quand j'ai vraiment besoin d'une seule classe. Je le fais par anticipation un jour, j'en aurais peut-être besoin et d'habitude je ne le fais jamais. Même quand je le fais, je trouve généralement 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 n'ai pas cette chance, 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 constater à un moment donné que tous les chats ne sont pas IPables (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 important que tout à coup les chats ne sont plus dérivés de PettableBase.
la source
Voici la définition de base et simple de l'interface et de la classe de base:
à votre santé
la source
Je recommande d'utiliser la composition au lieu de l'héritage dans la mesure du possible. Utilisez des interfaces mais utilisez des objets membres pour l'implémentation de base. De cette façon, vous pouvez définir une fabrique qui construit vos objets pour qu'ils se comportent d'une certaine manière. Si vous souhaitez modifier le comportement, vous créez une nouvelle méthode de fabrique (ou fabrique abstraite) qui crée différents types de sous-objets.
Dans certains cas, vous pouvez constater que vos objets principaux n'ont pas du tout besoin d'interfaces, si tout le comportement mutable est défini dans les objets d'assistance.
Ainsi, au lieu de IPet ou PetBase, vous pourriez vous retrouver avec un animal de compagnie 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é pour la méthode shed ().
Si vous faites cela, vous constaterez que votre code est beaucoup plus flexible et la plupart de vos objets simples traitent de comportements très basiques à l'échelle du système.
Je recommande ce modèle même dans les langages à héritage multiple.
la source
Il est bien expliqué dans cet article sur Java World .
Personnellement, j'ai tendance à utiliser des interfaces pour définir des interfaces - c'est-à-dire des parties de la conception du système qui spécifient comment accéder à quelque chose.
Il n'est pas rare que j'aie une classe implémentant une ou plusieurs interfaces.
Les classes abstraites que j'utilise comme base pour autre chose.
Ce qui suit est un extrait de l'article mentionné ci-dessus Article de JavaWorld.com, auteur Tony Sintes, 20/04/01
Certains disent que vous devriez définir toutes les classes en termes d'interfaces, mais je pense que la recommandation semble un peu extrême. J'utilise des interfaces lorsque je vois que quelque chose dans ma conception change fréquemment.
la source
Gardez également à l'esprit de ne pas vous laisser emporter par OO ( voir blog ) et modélisez toujours les objets en fonction du comportement requis, si vous concevez une application où le seul comportement dont vous avez besoin est un nom générique et une espèce pour un animal, alors vous n'aurez besoin que un animal de classe avec une propriété pour le nom, au lieu de millions de classes pour chaque animal possible dans le monde.
la source
J'ai une règle générale approximative
Fonctionnalité: susceptible d'être différente dans toutes les parties: Interface.
Les données et les fonctionnalités, les parties seront essentiellement les mêmes, les parties différentes: classe abstraite.
Les données et les fonctionnalités fonctionnent réellement, si elles ne sont étendues qu'avec de légers changements: classe ordinaire (concrète)
Données et fonctionnalités, aucun changement prévu: classe ordinaire (concrète) avec modificateur final.
Données et éventuellement fonctionnalité: lecture seule: membres enum.
C'est très approximatif et prêt et pas du tout strictement défini, mais il existe un spectre d'interfaces où tout est destiné à être changé en énumérations où tout est fixé un peu comme un fichier en lecture seule.
la source
Les interfaces doivent être petites. Vraiment petit. Si vous décomposez vraiment vos objets, 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 en finir? Si oui, alors il est temps pour une classe abstraite.
Les classes abstraites sont également limitantes. Bien qu'ils vous offrent un excellent raccourci pour produire des objets enfants, tout objet donné ne peut implémenter qu'une seule classe abstraite. Plusieurs fois, je trouve cela une limitation des classes abstraites, et c'est pourquoi j'utilise beaucoup d'interfaces.
Les classes abstraites peuvent contenir plusieurs interfaces. Votre classe abstraite PetBase peut implémenter IPet (les animaux ont des propriétaires) et IDigestion (les animaux mangent, ou du moins ils devraient). Cependant, PetBase n'implémentera probablement pas 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 un MammalPetBase qui étend PetBase et ajouter IMammal. FishBase pourrait avoir PetBase et ajouter IFish. IFish aurait des interfaces ISwim et IUnderwaterBreather.
Oui, mon exemple est largement trop compliqué pour l'exemple simple, mais cela fait partie de la grande chose sur la façon dont les interfaces et les classes abstraites fonctionnent ensemble.
la source
Source : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C # est un langage merveilleux qui a mûri et évolué au cours des 14 dernières années. C'est formidable pour nous, développeurs, car un langage mature nous fournit une pléthore de fonctionnalités linguistiques à notre disposition.
Cependant, avec beaucoup de pouvoir devient beaucoup de responsabilité. Certaines de ces fonctionnalités peuvent être utilisées à mauvais escient, ou il est parfois difficile de comprendre pourquoi vous choisissez d'utiliser une fonctionnalité plutôt qu'une autre. Au fil des ans, une caractéristique avec laquelle de nombreux développeurs ont eu du mal à choisir quand utiliser une interface ou choisir d'utiliser une classe abstraite. Les deux ont leurs avantages et leurs inconvénients et le bon moment et le bon endroit pour les utiliser. Mais comment décider ???
Les deux prévoient la réutilisation de fonctionnalités communes entre les types. La différence la plus évidente est que les interfaces ne fournissent aucune implémentation pour leur fonctionnalité tandis que les classes abstraites vous permettent d'implémenter un comportement «de base» ou «par défaut» et ont ensuite la possibilité de «remplacer» ce comportement par défaut avec les types dérivés de classes si nécessaire .
Tout cela est bien et bon et prévoit une grande réutilisation du code et adhère au principe de développement logiciel DRY (Don't Repeat Yourself). Les classes abstraites sont très utiles lorsque vous avez une relation «est une».
Par exemple: Un golden retriever «est un» type de chien. Un caniche aussi. Ils peuvent tous deux aboyer, comme tous les chiens. Cependant, vous voudrez peut-être déclarer que le parc caniche est significativement différent de l'écorce de chien "par défaut". À cet effet, il pourrait être judicieux pour vous de mettre en œuvre quelque chose comme suit:
Comme vous pouvez le voir, ce serait un excellent moyen de garder votre code SEC et de permettre l'implémentation de la classe de base lorsque l'un des types peut simplement s'appuyer sur Bark par défaut au lieu d'une implémentation de cas spécial. Les classes comme GoldenRetriever, Boxer, Lab pourraient toutes hériter gratuitement de l'écorce «par défaut» (classe de basse) simplement parce qu'elles implémentent la classe abstraite Dog.
Mais je suis sûr que vous le saviez déjà.
Vous êtes ici parce que vous voulez comprendre pourquoi vous pouvez choisir une interface plutôt qu'une classe abstraite ou vice versa. Eh bien, une raison pour laquelle vous pouvez choisir une interface plutôt qu'une classe abstraite est que vous n'avez pas ou ne voulez pas empêcher une implémentation par défaut. Cela est généralement dû au fait que les types qui implémentent l'interface ne sont pas liés dans une relation «est un». En fait, ils n'ont pas du tout besoin d'être liés, sauf que chaque type «est capable» ou a «la capacité» de faire quelque chose ou d'avoir quelque chose.
Maintenant, qu'est-ce que cela signifie? Eh bien, par exemple: un humain n'est pas un canard… et un canard n'est pas un humain. Assez évident. Cependant, à la fois un canard et un humain ont «la capacité» de nager (étant donné que l'humain a réussi ses cours de natation en 1re année :)). De plus, puisqu'un canard n'est pas un être humain ou vice versa, ce n'est pas une «est une» réalité, mais plutôt une relation «est capable» et nous pouvons utiliser une interface pour illustrer cela:
L'utilisation d'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 se soucie pas de la façon dont il le fait… Tout ce qu'il sait, c'est qu'il peut appeler la méthode Swim sur cet objet et cet objet saura quel comportement adopter au moment de 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 ici permet aux méthodes d'appel de ne pas avoir à se soucier de quel type est lequel ou comment le comportement est implémenté. Il sait juste que, étant donné l'interface, chaque objet devra avoir implémenté la méthode Swim, il est donc sûr de l'appeler dans son propre code et de permettre au comportement de la méthode Swim d'être géré dans sa propre classe.
Sommaire:
Donc, ma règle générale est d'utiliser une classe abstraite lorsque vous souhaitez implémenter une fonctionnalité "par défaut" pour une hiérarchie de classes ou / et les classes ou types avec lesquels vous travaillez partagent une relation "est une" (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 (par exemple, le canard «n'est pas» un humain. Cependant, le canard et la part humaine «La capacité» de nager).
Une autre différence à noter entre les classes abstraites et les interfaces est qu'une classe peut implémenter une à plusieurs interfaces, mais une classe ne peut hériter que d'une seule classe abstraite (ou de toute classe d'ailleurs). Oui, vous pouvez imbriquer des classes et avoir une hiérarchie d'héritage (ce que de nombreux programmes font et devraient avoir) mais vous ne pouvez pas hériter de deux classes dans une définition de classe dérivée (cette règle s'applique à C #. Dans certains autres langages, vous pouvez le faire, généralement uniquement à cause du manque d'interfaces dans ces langues).
Souvenez-vous également lorsque vous utilisez des interfaces pour adhérer au principe de ségrégation d'interface (ISP). Le FAI déclare qu'aucun client ne doit être contraint 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 (ex. IDisposable, IComparable).
Une autre astuce est que si vous développez de petites fonctionnalités concises, utilisez des interfaces. Si vous concevez de grandes unités fonctionnelles, utilisez une classe abstraite.
J'espère que cela clarifie les choses pour certaines personnes!
De plus, si vous pouvez penser à de meilleurs exemples ou si vous souhaitez signaler quelque chose, veuillez le faire dans les commentaires ci-dessous!
la source
Le cas des classes de base sur les interfaces a été bien expliqué dans les directives de codage de Submain .NET:
la source
Une différence importante est que vous ne pouvez hériter que d' une classe de base, mais vous pouvez implémenter de nombreuses interfaces. Vous ne souhaitez donc utiliser une classe de base que si vous êtes absolument certain que vous n'aurez pas besoin d'hériter également d'une classe de base différente. De plus, si vous trouvez que votre interface devient volumineuse, vous devriez commencer à chercher à la décomposer en quelques éléments logiques qui définissent des fonctionnalités indépendantes, car il n'y a pas de règle selon laquelle votre classe ne peut pas toutes les implémenter (ou que vous pouvez définir une autre interface qui les hérite tous pour les regrouper).
la source
Lorsque j'ai commencé à apprendre la programmation orientée objet, j'ai fait l'erreur facile et probablement courante d'utiliser l'héritage pour partager un comportement commun - même lorsque ce comportement n'était pas essentiel à la nature de l'objet.
Pour continuer sur un exemple très utilisé dans cette question particulière, il y a beaucoup de choses qui sont petable - copines, voitures, couvertures floues ... - donc j'aurais peut-être eu une classe Petable qui fournissait ce comportement commun, et diverses classes héritant à partir de cela.
Cependant, être petable ne fait partie de la nature d'aucun de ces objets. Il existe des concepts beaucoup plus importants qui sont essentiels à leur nature - la petite amie est une personne, la voiture est un véhicule terrestre, le chat est un mammifère ...
Les comportements doivent d'abord être attribués aux interfaces (y compris l'interface par défaut de la classe), et promus en classe de base uniquement s'ils sont (a) communs à un grand groupe de classes qui sont des sous-ensembles d'une classe plus grande - dans le même sens que "chat" et "personne" sont des sous-ensembles de "mammifère".
Le hic, c'est qu'après avoir compris la conception orientée objet suffisamment mieux que moi au début, vous le ferez normalement automatiquement sans même y penser. Ainsi, la simple vérité de l'énoncé «code vers une interface, pas une classe abstraite» devient si évidente que vous avez du mal à croire que quiconque se donnerait la peine de le dire - et commencez à essayer d'y lire d'autres significations.
Une autre chose que j'ajouterais est que si une classe est purement abstraite - sans membres ou méthodes non abstraits et non hérités exposés à l'enfant, au parent ou au client - alors pourquoi est-ce une classe? Il pourrait être remplacé, dans certains cas par une interface et dans d'autres cas par Null.
la source
Préférer les interfaces aux classes abstraites
Justification, les principaux points à considérer [deux déjà mentionnés ici] sont:
Bien sûr, le sujet a été longuement discuté ailleurs [2,3].
[1] Cela 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, articles 16-18.
[3] http://www.codeproject.com/KB/ar ...
la source
Les commentaires précédents sur l'utilisation de classes abstraites pour une implémentation commune sont définitivement d'actualité. Un avantage que je n'ai pas encore mentionné est que l'utilisation d'interfaces facilite beaucoup l'implémentation d'objets fictifs à des fins de test unitaire. Définir IPet et PetBase comme Jason Cohen l'a décrit vous permet de simuler facilement différentes conditions de données, sans la surcharge d'une base de données physique (jusqu'à ce que vous décidiez qu'il est temps de tester la réalité).
la source
N'utilisez pas une classe de base à moins que vous ne sachiez ce qu'elle signifie et qu'elle s'applique dans ce cas. Si cela s'applique, utilisez-le, sinon, utilisez des interfaces. Mais notez la réponse concernant les petites interfaces.
L'héritage public est surutilisé dans OOD et exprime beaucoup plus que ce que la plupart des développeurs réalisent ou sont prêts à vivre. Voir le principe de substituabilité de Liskov
En bref, si A "est un" B alors A ne nécessite pas plus de B et ne délivre pas moins de B, pour chaque méthode qu'il expose.
la source
Une autre option à garder à l'esprit est d'utiliser la relation "has-a", alias "est implémentée en termes de" ou "composition". Parfois, c'est un moyen plus propre et plus flexible de structurer les choses que d'utiliser l'héritage "is-a".
Logiquement, 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 héréditaires multiples:
Oui, cet exemple montre qu'il y a beaucoup de duplication de code et un manque d'élégance impliqués dans les choses de cette façon. Mais il faut également comprendre que cela aide à garder le chien et le chat découplés de la classe des animaux de compagnie (en ce que le chien et le chat n'ont pas accès aux membres privés de l'animal de compagnie), et cela laisse de la place pour le chien et le chat à hériter d'autre chose - -peut-être la classe des mammifères.
La composition est préférable quand aucun accès privé n'est requis et que vous n'avez pas besoin de faire référence à Dog and Cat à l'aide de 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 également brouiller les choses lorsqu'elles sont mal organisées. L'héritage est utile lorsque vous avez besoin d'un accès privé, et en l'utilisant, vous vous engagez à coupler fortement vos classes de chiens et de chats à votre classe d'animaux de compagnie, ce qui représente un coût élevé à payer.
Entre l'héritage, la composition et les interfaces, il n'y a pas qu'un seul moyen qui soit toujours juste, et cela aide à considérer comment les trois options peuvent être utilisées en harmonie. Parmi les trois, l'héritage est généralement l'option qui doit être utilisée le moins souvent.
la source
Conceptuellement, une interface est utilisée pour définir formellement et semi-formellement un ensemble de méthodes qu'un objet fournira. Signifie formellement un ensemble de noms de méthodes et de signatures, et signifie semi-formellement une 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 interface de programmation d' application ), elles ne peuvent contenir aucune implémentation et il n'est pas possible d'utiliser ou d'exécuter une interface. Ils expliquent uniquement le contrat sur la façon dont vous devez interagir avec un objet.
Les classes fournissent une implémentation et 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 consiste à préfixer le nom de la classe avec "Base".
Il existe une distinction entre une classe de base et une classe de base abstraite (ABC). Les ABC mélangent interface et implémentation ensemble. Abstrait en dehors de la programmation informatique signifie "résumé", c'est-à-dire "interface abstraite ==". Une classe de base abstraite peut alors décrire à la fois une interface, ainsi qu'une implémentation vide, partielle ou complète destinée à être héritée.
Avis sur le moment d'utiliser des interfaces par rapport aux classes abstraites de base par rapport à seulement les classes va varier énormément en fonction à la fois ce que vous développez, et la langue que vous développent dans. Les interfaces sont souvent associées uniquement avec les langages statiquement typés tels que Java ou C #, mais Les langages typés dynamiquement peuvent également avoir des interfaces et des classes de base abstraites . En Python par exemple, la distinction est clairement établie entre une classe, qui déclare qu'elle implémente une interface , et un objet, qui est une instance d'une classe , et qui est censé fournir cette interface. Il est possible dans un langage dynamique que deux objets qui sont tous les deux des instances de la même classe , puissent déclarer qu'ils fournissent des interfaces complètement différentes . En Python, cela n'est possible que pour les attributs d'objet, tandis 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, il est donc possible que l' interface entre deux objets de la même classe puisse varier autant que le souhaite le programmeur (cependant, Ruby n'a aucun moyen explicite de déclarer des interfaces).
Dans les langages dynamiques, l'interface avec un objet est souvent implicitement supposée, soit en introspectant un objet et en lui demandant quelles méthodes il fournit ( regardez avant de sauter ) ou de préférence en essayant simplement d'utiliser l' interface souhaitée sur un objet et en interceptant des exceptions si l'objet ne fournit pas cette interface ( plus facile de demander pardon que 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 car vous n'avez pas besoin de trop spécifier à l'avance pour anticiper toutes les utilisations possibles de votre code.
la source
Cela dépend de vos besoins. Si 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-y.
L'inconvénient de la mise en œuvre d'une classe de base est l'exigence de
override
(ounew
) des méthodes existantes. Cela en fait des méthodes virtuelles, ce qui signifie que vous devez faire attention à la façon dont vous utilisez l'instance d'objet.Enfin, l'héritage unique de .NET me tue. Un exemple naïf: supposons que vous créez un contrôle utilisateur, donc vous héritez
UserControl
. Mais, maintenant, vous êtes également exclu de l'héritagePetBase
. Cela vous oblige à réorganiser, par exemple à faire unPetBase
membre de la classe, à la place.la source
Je n'implémente généralement pas non plus avant d'en avoir 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 des classes héritées, je le déplace et crée une classe de base abstraite. Je ne vois pas la nécessité des deux, car ils servent essentiellement le même but, et avoir les deux est une mauvaise odeur de code (à mon humble avis) que la solution a été sur-conçue.
la source
En ce qui concerne C #, dans certains sens, les interfaces et les classes abstraites peuvent être interchangeables. Cependant, les différences sont les suivantes: i) les interfaces ne peuvent pas implémenter de 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, tandis que plusieurs interfaces peuvent être implémentées sur une classe.
la source
Par défaut, l'interface fournit une couche pour communiquer avec un autre code. Toutes les propriétés et méthodes publiques d'une classe implémentent par défaut une interface implicite. Nous pouvons également définir une interface comme un rôle, chaque fois qu'une classe doit jouer ce rôle, elle doit l'implémenter en lui donnant différentes formes d'implémentation en fonction de la classe qui l'implémente. Par conséquent, lorsque vous parlez d'interface, vous parlez de polymorphisme et lorsque vous parlez de classe de base, vous parlez d'héritage. Deux concepts de oups !!!
la source
J'ai trouvé qu'un modèle d'Interface> Abstrait> Béton fonctionne dans le cas d'utilisation suivant:
La classe abstraite définit les attributs partagés par défaut des classes concrètes, mais applique l'interface. Par exemple:
Maintenant, puisque tous les mammifères ont des cheveux et des mamelons (AFAIK, je ne suis pas zoologiste), nous pouvons les intégrer dans la classe de base abstraite
Et puis les classes concrètes définissent simplement qu'elles marchent debout.
Cette conception est agréable quand il y a beaucoup de classes concrètes et que vous ne voulez pas maintenir le passe-partout juste pour programmer sur une interface. Si de nouvelles méthodes étaient ajoutées à l'interface, cela briserait toutes les classes résultantes, vous bénéficiez donc toujours des avantages de l'approche d'interface.
Dans ce cas, l'abrégé pourrait tout aussi bien être concret; cependant, la désignation abstraite permet de souligner que ce modèle est utilisé.
la source
Un héritier d'une classe de base doit avoir une relation «est une». L'interface représente une relation «implémente une». N'utilisez donc une classe de base que lorsque vos héritiers maintiendront la relation is.
la source
Utilisez les interfaces pour appliquer un contrat dans les familles de classes non lié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 jeu de résultats d'une requête, tandis que l'autre peut représenter les images dans une galerie. En outre, vous pouvez implémenter plusieurs interfaces, vous permettant ainsi de mélanger (et de signifier) les capacités de la classe.
Utilisez l'héritage lorsque les classes ont une relation commune et ont donc une signature structurelle et comportementale similaire, c'est-à-dire que la voiture, la moto, le camion et le SUV sont tous des types de véhicules routiers qui peuvent contenir un certain nombre de roues, une vitesse de pointe
la source