"Classe abstraite" et "interface" sont des concepts similaires, l'interface étant la plus abstraite des deux. Un facteur de différenciation est que les classes abstraites fournissent des implémentations de méthodes pour les classes dérivées lorsque cela est nécessaire. En C #, toutefois, ce facteur de différenciation a été réduit par l’introduction récente de méthodes d’extension, qui permettent de fournir des implémentations pour les méthodes d’interface. Un autre facteur de différenciation est qu'une classe ne peut hériter que d'une classe abstraite (c'est-à-dire qu'il n'y a pas d'héritage multiple), mais qu'elle peut implémenter plusieurs interfaces. Cela rend les interfaces moins restrictives et plus flexibles. Donc, en C #, quand devrions-nous utiliser des classes abstraites au lieu d’interfaces avec des méthodes d’extension?
LINQ est un exemple notable de modèle de méthode interface + extension: la fonctionnalité de requête est fournie pour tout type implémenté IEnumerable
via une multitude de méthodes d'extension.
la source
Réponses:
Lorsque vous avez besoin d'une partie de la classe à implémenter. Le meilleur exemple que j'ai utilisé est le modèle de méthode template .
Ainsi, vous pouvez définir les étapes à suivre lors de l'appel de Do (), sans connaître les spécificités de leur implémentation. Les classes dérivées doivent implémenter les méthodes abstraites, mais pas la méthode Do ().
Les méthodes d'extensions ne répondent pas nécessairement à la partie "doivent faire partie de la classe" de l'équation. De plus, les méthodes d'extension iirc ne peuvent (sembler être) rien d'autre que public.
modifier
La question est plus intéressante que ce que je pensais à l’origine. Après un examen plus approfondi, Jon Skeet a répondu à une question comme celle-ci sur SO en faveur de l'utilisation d'interfaces + méthodes d'extension. En outre, un inconvénient potentiel consiste à utiliser la réflexion par rapport à une hiérarchie d'objets conçue de cette manière.
Personnellement, j'ai du mal à voir l'intérêt de modifier une pratique actuellement courante, mais je ne vois également que peu ou pas d'inconvénients à le faire.
Il est à noter qu'il est possible de programmer de cette manière dans de nombreuses langues via les classes utilitaires. Les extensions fournissent simplement le sucre syntaxique pour donner l’apparence des méthodes à la classe.
la source
Do()
seront définies comme une méthode d’extension. Et les méthodes laissées pour la définition des classes dérivéesDoThis()
seront comme membres de l'interface principale. Et avec les interfaces, vous pouvez prendre en charge plusieurs implémentations / héritages. Et voyez ceciTout dépend de ce que vous voulez modéliser:
Les classes abstraites fonctionnent par héritage . S'agissant uniquement de classes de base spéciales, ils modélisent une relation.
Par exemple, un chien est un animal, nous avons donc
Comme il n’a pas de sens de créer un animal générique générique (à quoi cela ressemblerait-il?), Nous fabriquons cette
Animal
classe de baseabstract
- mais c’est toujours une classe de base. Et laDog
classe n'a pas de sens sans être unAnimal
.Les interfaces en revanche sont une autre histoire. Ils n'utilisent pas l'héritage mais fournissent un polymorphisme (qui peut également être implémenté avec l'héritage). Ils ne modélisent pas une relation is-a , mais plus d'une relation prend en charge .
Prenons par exemple
IComparable
un objet qui peut être comparé à un autre .La fonctionnalité d'une classe ne dépend pas des interfaces qu'elle implémente, l'interface fournit simplement un moyen générique d'accéder à la fonctionnalité. Nous pourrions encore
Dispose
d'unGraphics
ou d'unFileStream
sans les avoir mettre en œuvreIDisposable
. L' interface ne fait que relier les méthodes.En principe, on pourrait ajouter et supprimer des interfaces comme des extensions sans modifier le comportement d'une classe, mais simplement en enrichir l'accès. Vous ne pouvez cependant pas enlever une classe de base car l'objet deviendrait sans signification!
la source
Animal
abstrait. Cela vous permet d'écrire desabstract
fonctions à spécialiser par les classes dérivées. Ou plus en termes de programmation - il ne fait probablement pas de sens pour créer unUserControl
, mais commeButton
est unUserControl
, nous avons donc le même genre de relation classe / baseclass.Je viens de me rendre compte que je n'ai pas utilisé de classes abstraites (du moins pas créées) depuis des années.
La classe abstraite est une bête quelque peu étrange entre l'interface et la mise en œuvre. Il y a quelque chose dans les classes abstraites qui picotent mon sens de l'araignée. Je dirais qu'il ne faut en aucun cas "exposer" la mise en oeuvre derrière l'interface (ou faire des liens).
Même s'il est nécessaire d'avoir un comportement commun entre les classes implémentant une interface, j'utiliserais une classe d'assistance indépendante, plutôt qu'une classe abstraite.
Je voudrais aller pour les interfaces.
la source
IMHO, je pense qu'il y a un mélange de concepts ici.
Les classes utilisent un certain type d'héritage, tandis que les interfaces utilisent un autre type d'héritage.
Les deux types d'héritage sont importants et utiles, et chacun a ses avantages et ses inconvénients.
Commençons par le début.
L'histoire avec les interfaces commence il y a longtemps avec C ++. Au milieu des années 1980, lorsque le C ++ a été développé, l'idée d'interfaces en tant que types différents n'était pas encore concrétisée (elle viendrait avec le temps).
C ++ n'avait qu'un seul type d'héritage, le type d'héritage utilisé par les classes, appelé héritage d'implémentation.
Pour pouvoir appliquer la recommandation du livre GoF: "Programmer pour l'interface, et non pour l'implémentation", les classes abstraites prises en charge par C ++ et l'héritage de plusieurs applications pour pouvoir faire quelles interfaces en C # sont capables (et utiles!) .
C # introduit un nouveau type de type, des interfaces, et un nouveau type d'héritage, l'héritage d'interface, afin d'offrir au moins deux manières différentes de prendre en charge "Pour programmer pour l'interface, et non pour l'implémentation", avec une mise en garde importante: C # uniquement prend en charge l'héritage multiple avec l'héritage d'interface (et non avec l'héritage d'implémentation).
Ainsi, les raisons architecturales derrière les interfaces en C # sont la recommandation du GoF.
J'espère que cela peut être utile.
Cordialement, Gastón
la source
L'application de méthodes d'extension à une interface est utile pour appliquer un comportement commun à des classes pouvant partager uniquement une interface commune. De telles classes peuvent déjà exister et être fermées aux extensions par d'autres moyens.
Pour les nouvelles conceptions, je privilégierais l’utilisation de méthodes d’extension sur les interfaces. Pour une hiérarchie de types, les méthodes d'extension fournissent un moyen d'étendre le comportement sans avoir à ouvrir toute la hiérarchie pour modification.
Cependant, les méthodes d'extension ne peuvent pas accéder à l'implémentation privée, donc au sommet d'une hiérarchie, si j'ai besoin d'encapsuler une implémentation privée derrière une interface publique, une classe abstraite serait alors le seul moyen.
la source
Je pense qu'en C #, l'héritage multiple n'est pas pris en charge et c'est pourquoi le concept d'interface est introduit en C #.
Dans le cas de classes abstraites, l'héritage multiple n'est pas non plus pris en charge. Il est donc facile de choisir d'utiliser des classes abstraites ou des interfaces.
la source
Je suis en train d'implémenter une classe Abstract dans mon projet simplement parce que C # ne prend pas en charge les opérateurs (conversions implicites, dans mon cas) sur les interfaces.
la source