web-dev-qa-db-fra.com

Quand utiliser des classes abstraites au lieu d'interfaces avec des méthodes d'extension en C #?

"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éthode pour les classes dérivées en cas de besoin. En C #, cependant, 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 qu'une seule classe abstraite (c'est-à-dire qu'il n'y a pas d'héritage multiple), mais 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?

Un exemple notable du modèle de méthode interface + extension est LINQ, où la fonctionnalité de requête est fournie pour tout type qui implémente IEnumerable via une multitude de méthodes d'extension.

70
Gulshan

Lorsque vous avez besoin d'une partie de la classe à implémenter. Le meilleur exemple que j'ai utilisé est le modèle méthode du modèle .

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

Ainsi, vous pouvez définir les étapes qui seront suivies lors de l'appel de Do (), sans connaître les détails 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 satisfont pas nécessairement la partie "doit faire partie de la classe" de l'équation. De plus, iirc, les méthodes d'extension ne peuvent (semblent) avoir qu'une portée publique.

modifier

La question est plus intéressante que je ne l'avais initialement cru. Après un examen plus approfondi, Jon Skeet a répond une question comme celle-ci sur SO en faveur de l'utilisation des interfaces + méthodes d'extension. En outre, un inconvénient potentiel utilise la réflexion contre une hiérarchie d'objets conçue de cette façon.

Personnellement, j'ai du mal à voir l'avantage de modifier une pratique actuellement courante, mais je vois aussi peu ou pas d'inconvénients à le faire.

Il est à noter qu'il est possible de programmer de cette façon dans de nombreuses langues via les classes Utility. Les extensions fournissent simplement le sucre syntaxique pour donner l'impression que les méthodes appartiennent à la classe.

63
Steven Evers

Tout dépend de ce que vous voulez modéliser:

Les classes abstraites fonctionnent par héritage. Étant simplement des classes de base spéciales, elles modélisent certaines relations is-a -.

Par exemple, un chien est un animal, nous avons donc

class Dog : Animal { ... }

Comme cela n'a aucun sens de réellement créer un tout-animal générique (à quoi cela ressemblerait-il?), Nous faisons ceci Animal classe de base abstract - mais c'est toujours une classe de base. Et la classe Dog n'a pas de sens sans être une Animal.

Interfaces d'autre part, c'est une autre histoire. Ils n'utilisent pas l'héritage mais fournissent polymorphisme (qui peut être implémenté avec l'héritage aussi). Ils ne modélisent pas une relation is-a , mais plutôt une prise en charge .

Prenez par exemple IComparable - un objet qui prend en charge la comparaison avec 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 toujours Dispose d'un Graphics ou d'un FileStream sans les faire implémenter IDisposable. L'interface relie simplement les méthodes ensemble.

En principe, on pourrait ajouter et supprimer des interfaces comme des extensions sans changer le comportement d'une classe, en enrichissant simplement l'accès à celle-ci. Vous ne pouvez cependant pas supprimer une classe de base car l'objet deviendrait vide de sens!

25
Dario

Je viens de réaliser 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 l'implémentation. Il y a quelque chose dans les classes abstraites qui me fait vibrer le sens de l'araignée. Je dirais qu'il ne faut en aucun cas "exposer" l'implémentation derrière l'interface (ou faire des liens).

Même s'il existe un besoin de comportement commun entre les classes implémentant une interface, j'utiliserais une classe d'assistance indépendante, plutôt qu'une classe abstraite.

J'irais pour les interfaces.

3
Maglob

À mon humble avis, 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 des interfaces commence depuis longtemps avec C++. Au milieu des années 1980, lorsque C++ a été développé, l'idée des interfaces en tant que type différent de type n'était pas encore réalisé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 du GoF: "Pour programmer pour l'interface, et non pour l'implémentation", les classes abstraites prises en charge par C++ et l'héritage d'implémentations multiples sont capables de faire ce que les interfaces en C # sont capables (et utiles!) De faire .

C # introduit un nouveau type de type, des interfaces et un nouveau type d'héritage, l'héritage d'interface, pour offrir au moins deux façons 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

2

L'application de méthodes d'extension à une interface est utile pour appliquer un comportement commun à toutes les classes qui peuvent partager uniquement une interface commune. Ces classes peuvent déjà exister et être fermées aux extensions par d'autres moyens.

Pour les nouvelles conceptions, je préférerais 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 la hiérarchie entière 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 l'implémentation privée derrière une interface publique, alors une classe abstraite serait le seul moyen.

1
Ed James

Je pense qu'en C # l'héritage multiple n'est pas supporté 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 pris en charge non plus. Il est donc facile de décider d'utiliser des classes abstraites ou des interfaces.

0
Beginner