Interface vs classe de base

767

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.

hurleur
la source
11
Juste un point que je pense que vous devriez prendre en considération - les interfaces peuvent poser plusieurs limites que vous ne connaissez peut-être pas avant des étapes très tardives. Par exemple, avec .NET, vous ne pouvez pas sérialiser une variable membre d'interface, donc si vous avez une classe Zoo et un tableau de variables membres d'IAnimals, vous ne pourrez pas sérialiser Zoo (et cela signifie que l'écriture de WebServices ou d'autres choses nécessitant une sérialisation serait une douleur).
synhershko le
1
Cette question pourrait aider à comprendre le concept des interfaces. stackoverflow.com/q/8531292/1055241
gprathour
Je suis juste curieux. J'ai rencontré dans le CLR via C # l'extrait suivant: 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?
qqqqqqq

Réponses:

501

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:

public abstract class Mammal

Cette classe de base aura probablement des méthodes par défaut telles que:

  • Alimentation
  • Camarade

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:

public class Dog : Mammal
public class Cat : Mammal

Supposons maintenant qu'il existe d'autres mammifères, que nous verrons généralement dans un zoo:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Ce sera toujours valable car au cœur de la fonctionnalité Feed()et Mate()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:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

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 à:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

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 .

Jon Limjap
la source
149
Je ne pense pas que ce soit aussi simple que cela. Vous avez légèrement modifié la question (exigences) pour que l'interface ait plus de sens. Vous devez toujours vous demander si vous définissez un contrat (interface) ou une implémentation partagée (classe de base).
David Pokluda
18
L'interface est un contrat. Vous exposez uniquement la partie du contrat dont un service a besoin. Si vous avez un «PettingZoo», vous ne voulez certainement pas exposer «Mate» à l'utilisateur!
Anthony Mastrean
10
@David Touche, même si je l'ai fait pour mieux illustrer à quoi sert une interface et à quoi sert une classe abstraite face à sa compréhension. Un chien et un chat ne semblent pas être une exigence rigide!
Jon Limjap
2
Il convient de noter que dans les environnements JIT interprétés (en particulier la JVM), les appels de méthode virtuelle sont nettement plus rapides que les appels de méthode d'interface , selon le contexte. J'insiste sur le «contexte» car la JVM peut souvent optimiser les recherches de méthodes lentes. (par exemple, si une liste d'héritiers d'interfaces a tendance à être des instances de la même classe. Cela rend tristement difficile un peu de benchmarking.) Si vous essayez d'optimiser quelque chose de sensible aux performances, et seulement alors, vous devriez considérer cela.
Philip Guin
14
De plus, une interface permet à un animal de compagnie de se baigner et de lui apprendre des astuces, mais l'alimentation et l'accouplement ne seraient pas pris en charge car ce serait ridicule pour un rocher.
eclux
146

Eh bien, Josh Bloch s'est dit dans Effective Java 2d :

Préférer les interfaces aux classes abstraites

Quelques points principaux:

  • Les classes existantes peuvent être facilement mises à niveau pour implémenter une nouvelle interface . Tout ce que vous avez à faire est d'ajouter les méthodes requises si elles n'existent pas encore et d'ajouter une clause implements à la déclaration de classe.

  • Les interfaces sont idéales pour définir des mixins . En gros, un mixin est un type qu'une classe peut implémenter en plus de son «type primaire» pour déclarer qu'il fournit un comportement facultatif. Par exemple, Comparable est une interface mixin qui permet à une classe de déclarer que ses instances sont ordonnées par rapport à d'autres objets mutuellement comparables.

  • Les interfaces permettent la construction de cadres de type non hiérarchiques . Les hiérarchies de types sont idéales pour organiser certaines choses, mais d'autres ne tombent pas parfaitement dans une hiérarchie rigide.

  • Les interfaces permettent des améliorations de fonctionnalités sûres et puissantes via l'idiome de classe enveloppante. Si vous utilisez des classes abstraites pour définir des types, vous laissez le programmeur qui souhaite ajouter des fonctionnalités sans autre alternative que d'utiliser l'héritage.

De plus, vous pouvez combiner les vertus des interfaces et des classes abstraites en fournissant une classe d'implémentation squelettique abstraite pour accompagner chaque interface non triviale que vous exportez.

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é.

Marcio Aguiar
la source
71
À chaque fois qu'une modification doit être apportée à une interface, la manière irréprochable de le faire est de créer une nouvelle interface qui hérite de l'ancienne. Cela préserve les implémentations existantes et vous permet de faire tout ce que vous souhaitez dans la nouvelle.
Scott Lawrence
5
nous avons le "principe de ségrégation d'interface". Ce principe nous apprend à prendre soin de la façon dont nous écrivons nos interfaces. Lorsque nous écrivons nos interfaces, nous devons veiller à n'ajouter que les méthodes qui devraient exister. Si nous ajoutons des méthodes qui ne devraient pas exister, les classes implémentant l'interface devront également implémenter ces méthodes. Par exemple, si nous créons une interface appelée Worker et ajoutons une méthode pause déjeuner, tous les travailleurs devront l'implémenter. Et si le travailleur est un robot? En conclusion Les interfaces contenant des méthodes qui ne lui sont pas spécifiques sont appelées interfaces polluées ou graisseuses.
Eklavyaa
Depuis Java 8, les méthodes par défaut vous permettent d'ajouter de nouvelles fonctionnalités aux interfaces et d'assurer une compatibilité descendante pour les classes existantes qui implémentent cette interface. Les méthodes par défaut sont appelées par défaut, si elles ne sont pas remplacées dans la classe d'implémentation. Toutes les classes d'implémentation peuvent remplacer les méthodes par défaut ou les appeler directement à l'aide de instance.defaultMethod ()
Arpit Rastogi
118

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 " Fooest jetable", d'où l' IDisposableinterface en C #.

Thomas Danecker
la source
10
De toutes les réponses, celle-ci donne le meilleur mélange de brièveté sans perte de clarté
Matt Wilko
Quelqu'un m'a dit une fois d'utiliser une interface quand il y a une relation "has-a". Je ne sais pas si c'est toujours vrai cependant; Mon ordinateur portable a un écran, alors l'ordinateur portable devrait-il implémenter IScreen ou avoir une propriété Screen? Ce dernier me semble plus naturel.
Berend
1
@berend drôle que vous écrivez cela parce que les écrans sont implémentés via des interfaces - VGA, HDMI, etc.
Konstantin
Tout simplement parce que c'est la POO, nous devons étirer notre imagination pour suivre des scénarios réels. Cela ne s'applique pas toujours.
Crismogram
110

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.

Jason Cohen
la source
prenez votre gâteau et mangez-le aussi!
Darren Kopp,
3
Une interface définit comment les autres classes peuvent utiliser votre code. Une classe de base aide les implémenteurs à implémenter votre interface. Deux choses différentes pour deux fins différentes.
Bill Michell,
27
Il n'y a rien de "moderne" ici. La classe de base et l'interface avec la même API sont simplement redondantes. Vous pouvez utiliser cette approche dans certains cas mais vous ne devriez pas généraliser!
smentek
3
Le commentaire le plus appuyé à la deuxième réponse appuyée n'est en fait pas d'accord avec la réponse elle-même, intéressante. Je dois dire que je vois de nombreux exemples d'interface et de classe de base coexister. En ce sens, c'est la voie "moderne". Par exemple, dans le modèle MVVM, il existe en fait la classe ViewModelBase qui implémente INotifyPropertyChanged. Mais quand mon collègue m'a demandé pourquoi j'avais la classe de base, au lieu d'implémenter l'interface dans chaque modèle de vue, je ne sais pas comment le convaincre
tete
1
Correct. Il ne s'agit ni de l'un ni de l'autre. Ils existent pour répondre à 2 problèmes très différents. Les interfaces sont des contrats qu'une classe d'implémentation doit traiter. Ils ont trouvé une faveur récente (parfois fanatique) pour les aspects IoC et TDD. Les classes abstraites / de base servent à regrouper une logique et des propriétés hiérarchiquement communes. Il réduit le code dupliqué, ce qui augmente la maintenabilité des solutions et le rend moins sujet aux erreurs.
ComeIn
63

Interfaces

  • Définit le contrat entre 2 modules. Ne peut avoir aucune implémentation.
  • La plupart des langues vous permettent d'implémenter plusieurs interfaces
  • La modification d'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.
  • Les interfaces aident au découplage. Vous pouvez utiliser des frameworks simulés pour simuler quoi que ce soit derrière une interface
  • Les interfaces indiquent normalement une sorte de comportement
  • Les implémentations d'interface sont découplées / isolées les unes des autres

Classes de base

  • Vous permet d'ajouter une implémentation par défaut que vous obtenez gratuitement par dérivation
  • À l'exception de C ++, vous ne pouvez dériver que d'une seule classe. Même si cela pouvait provenir de plusieurs classes, c'est généralement une mauvaise idée.
  • Changer la classe de base est relativement facile. Les dérivations n'ont rien à faire de spécial
  • Les classes de base peuvent déclarer des fonctions publiques et protégées accessibles par des dérivations
  • Les classes de base abstraites ne peuvent pas être simulées facilement comme les interfaces
  • Les classes de base indiquent normalement la hiérarchie des types (IS A)
  • Les dérivations de classe peuvent en venir à dépendre de certains comportements de base (avoir une connaissance complexe de l'implémentation parent). Les choses peuvent être compliquées si vous modifiez l'implémentation de base pour un gars et cassez les autres.
Gishu
la source
Remarque: les directives de conception du framework recommandent d'utiliser des classes de base (par opposition aux interfaces) car elles version mieux. L'ajout d'une nouvelle méthode à une classe de base abstraite dans vNext est un changement incessant.
Gishu
59

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.

Kilhoffer
la source
Yeesh. Personnellement, je pense que c'est à l'envers. Les interfaces doivent contenir la fonctionnalité minimale pour un type et les classes de base doivent fournir un cadre riche sur lequel la personnalisation peut être construite. Mettez cela dans une interface et vous avez rendu la mise en œuvre très difficile.
Personne n'a dit que vos interfaces devaient être énormes. Des interfaces plus petites et multiples et des classes de base plus riches constituent une excellente API.
Kilhoffer
3
Est-ce juste moi ou la plupart des classes de "travailleurs communs" partagent-elles une implémentation commune? Dans ce contexte, cela va à l'encontre de votre règle générale de privilégier les interfaces. Je reformulerais votre généralisation en 2 généralisations: ces classes communes qui ne contiennent pas ou peu de logique devraient implémenter une interface. Ces classes communes qui contiennent une quantité "décente" de logique devraient dériver d'une classe de base (car très probablement elles partageront des fonctionnalités).
goku_da_master
@Kilhoffer "Les interfaces permettent une flexibilité et une portabilité maximales des types à travers les frontières", veuillez élaborer cette déclaration.
JAVA
49

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:

Au cours des trois versions du .NET Framework, j'ai parlé de cette directive avec un certain nombre de développeurs de notre équipe. Beaucoup d'entre eux, y compris ceux qui étaient initialement en désaccord avec les directives, ont dit qu'ils regrettaient d'avoir livré une API en tant qu'interface. Je n'ai pas entendu parler d'un seul cas dans lequel quelqu'un a regretté d'avoir expédié un cours.

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.

fryguybob
la source
19

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.

Flory
la source
18

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 = héritage fonctionnel.

à votre santé

RWendi
la source
12

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.

Joe
la source
12

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


Interface vs classe abstraite

Le choix des interfaces et des classes abstraites n'est pas une proposition ni l'un ni l'autre. Si vous devez changer votre conception, faites-en une interface. Cependant, vous pouvez avoir des classes abstraites qui fournissent un comportement par défaut. Les classes abstraites sont d'excellents candidats à l'intérieur des cadres d'application.

Les classes abstraites vous permettent de définir certains comportements; ils forcent vos sous-classes à en fournir d'autres. Par exemple, si vous disposez d'un cadre d'application, une classe abstraite peut fournir des services par défaut tels que la gestion des événements et des messages. Ces services permettent à votre application de se connecter à votre infrastructure d'application. Cependant, il existe des fonctionnalités spécifiques à l'application que seule votre application peut effectuer. Ces fonctionnalités peuvent inclure des tâches de démarrage et d'arrêt, qui dépendent souvent 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 abstraites d'arrêt et de démarrage. La classe de base sait qu'elle a besoin de ces méthodes, mais une classe abstraite permet à votre classe d'admettre qu'elle ne sait pas comment effectuer ces actions; il sait seulement qu'il doit initier les actions. Quand il est temps de démarrer, la classe abstraite peut appeler la méthode de démarrage. Lorsque la classe de base appelle cette méthode, Java appelle la méthode définie par la classe enfant.

De nombreux développeurs oublient qu'une classe qui définit une méthode abstraite peut également 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-feuilles dans les hiérarchies de classes.

Classe vs interface

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.

Par exemple, le modèle 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 comment lire des CD, des MP3 et des fichiers wav. Bien sûr, vous ne voulez pas coder en dur ces algorithmes de lecture dans le lecteur; cela rendra difficile l'ajout d'un nouveau format comme AVI. De plus, votre code sera jonché de déclarations de cas inutiles. Et pour ajouter l'insulte à la blessure, vous devrez mettre à jour ces déclarations de cas chaque fois que vous ajoutez un nouvel algorithme. Dans l'ensemble, ce n'est pas une façon très orientée objet de programmer.

Avec le modèle de stratégie, vous pouvez simplement encapsuler l'algorithme derrière un objet. Si vous le faites, vous pouvez à tout moment fournir de nouveaux plug-ins multimédias. Appelons la classe de plug-in MediaStrategy. Cet objet aurait une méthode: playStream (Stream s). Donc, pour ajouter un nouvel algorithme, nous étendons simplement notre classe d'algorithmes. Désormais, lorsque le programme rencontre le nouveau type de média, il délègue simplement la lecture du flux à notre stratégie média. Bien sûr, vous aurez besoin 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 modèle de stratégie, qui indique clairement une place dans la conception qui changera. Ainsi, vous devez définir la stratégie comme une interface. Vous devez généralement privilégier les interfaces à l'héritage lorsque vous souhaitez qu'un objet ait un certain type; dans ce cas, MediaStrategy. Se fier à l'héritage pour l'identité de type est dangereux; il vous enferme dans une hiérarchie d'héritage particulière. Java n'autorise pas l'héritage multiple, vous ne pouvez donc pas étendre quelque chose qui vous donne une implémentation utile ou plus d'identité de type.

Richard Harrison
la source
2
+1. "Se fier à l'héritage pour l'identité de type est dangereux; il vous enferme dans une hiérarchie d'héritage particulière." Cette phrase décrit parfaitement mes raisons de préférer les interfaces.
Ingénieur
Et au-delà de cela, plutôt que d'étendre, composez l'implémentation derrière chaque méthode de votre interface.
Ingénieur
10

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.

Sijin
la source
10

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.

Akhil Jain
la source
7

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.

Jarrett Meyer
la source
7

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:

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 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:

// 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!

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!

Jason Roell
la source
6

Le cas des classes de base sur les interfaces a été bien expliqué dans les directives de codage de Submain .NET:

Classes de base vs interfaces Un type d'interface est une description partielle d'une valeur, potentiellement prise en charge par de nombreux types d'objets. Utilisez des classes de base au lieu d'interfaces autant que possible. Du point de vue du contrôle de version, les classes sont plus flexibles que les interfaces. Avec une classe, vous pouvez expédier la version 1.0, puis dans la version 2.0 ajouter une nouvelle méthode à la classe. Tant que la méthode n'est pas abstraite, toutes les classes dérivées existantes continuent de fonctionner inchangées.

Étant donné que les interfaces ne prennent pas en charge l'héritage d'implémentation, le modèle qui s'applique aux classes ne s'applique pas aux interfaces. Ajouter une méthode à une interface équivaut à ajouter une méthode abstraite à une classe de base; toute classe qui implémente l'interface sera interrompue car la classe n'implémente pas la nouvelle méthode. Les interfaces sont appropriées dans les situations suivantes:

  1. Plusieurs classes indépendantes souhaitent prendre en charge le protocole.
  2. Ces classes ont déjà des classes de base établies (par exemple, certaines sont des contrôles d'interface utilisateur (UI) et d'autres sont des services Web XML).
  3. L'agrégation n'est ni appropriée ni réalisable. Dans toutes les autres situations, l'héritage de classe est un meilleur modèle.
YeahStu
la source
Je pense que cette réponse devrait attirer davantage l'attention. Cela va à l'encontre de nombreuses réponses, ici. Je ne dirais pas que je suis entièrement d'accord, mais il y a de grands points ici.
kayleeFrye_onDeck
5

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).

Joel Coehoorn
la source
4

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.

Don Edwards
la source
Une classe purement abstraite peut fournir des comportements par défaut pour les méthodes. Ceci est utile lorsque vos classes concrètes partageront toutes des méthodes communes qui seraient redondantes à réimplémenter encore et encore.
Adam Hughes
4

Préférer les interfaces aux 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 interfaces. Étant donné que Java n'a pas d'héritage multiple, l'utilisation de classes abstraites empêche vos utilisateurs d'utiliser toute autre hiérarchie de classes. En général, préférez les interfaces lorsqu'il n'y a aucune implémentation ou état par défaut. Les collections Java en offrent de bons exemples (Map, Set, etc.).
  • Les classes abstraites ont l'avantage de permettre une meilleure compatibilité ascendante. Une fois que les clients utilisent une interface, vous ne pouvez pas la modifier; s'ils utilisent une classe abstraite, vous pouvez toujours ajouter un comportement sans casser le code existant. Si la compatibilité est un problème, envisagez d'utiliser des classes abstraites.
  • Même si vous avez des implémentations par défaut ou un état interne, pensez à proposer une interface et une implémentation abstraite de celle-ci . Cela aidera les clients, mais leur permettra toujours une plus grande liberté si désiré [1].
    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 ...

Jaimin Patel
la source
3

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é).

Scott Lawrence
la source
3

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.

theschmitzer
la source
3

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:

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 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.

Parappa
la source
3

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.

blé
la source
2

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(ou new) 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éritage PetBase. Cela vous oblige à réorganiser, par exemple à faire un PetBasemembre de la classe, à la place.

spoulson
la source
2

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.

Bill le lézard
la source
2

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.

CarneyCode
la source
2

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 !!!

Ganesh Kodiganti
la source
2

J'ai trouvé qu'un modèle d'Interface> Abstrait> Béton fonctionne 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, mais applique l'interface. Par exemple:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

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

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 puis les classes concrètes définissent simplement 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 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é.

Adam Hughes
la source
1

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.

Aaron Fischer
la source
1

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

sunwukung
la source