Classes abstraites vs interfaces

Je suis un peu confus quant à l’utilisation des classes abstraites en C #. En C ++, il est logique de définir un modèle que les classes héritant de la classe abstraite peuvent suivre. Mais, en C #, l’interface n’at-elle pas le même objective?

Il est vrai que les classes abstraites peuvent avoir une implémentation par défaut qui n’est pas fournie par les interfaces. Donc, si l’implémentation n’a pas besoin d’être incluse dans la classe de base, vaut-il mieux opter pour les interfaces?

J’aime toujours fournir une implémentation abstraite par défaut d’une interface, en supposant que ce soit une interface substantielle (et cela a du sens). Vous ne savez jamais quand vous pourriez append quelque chose à l’interface ayant une implémentation par défaut facile à inclure et à donner “gratuitement” à toute personne héritant de la classe de base abstraite.

Cet article CodeProject contient de nombreuses informations sur la différence entre les deux, notamment un tableau comparant les caractéristiques de chacun.

Les interfaces définissent le contrat entre les classes – la façon dont les classes s’appellent. Une classe peut implémenter plusieurs interfaces, mais ne peut hériter que d’une classe abstraite.

Il est vrai que les classes abstraites peuvent avoir une implémentation par défaut qui n’est pas fournie par les interfaces. Donc, si l’implémentation n’a pas besoin d’être incluse dans la classe de base, vaut-il mieux opter pour les interfaces?

Oui :). S’il est logique d’implémenter des méthodes dans la classe de base qui seront communes à toutes les classes héritées, vous devriez utiliser une classe abstraite. Si la classe de base sert uniquement à définir une interface mais qu’il n’y a pas de logique commune entre les classes héritées, utilisez une interface.

Pour votre première question, oui.

Pour votre deuxième réponse, je vais vous donner quelques conseils que j’ai suivis.

  • Utilisez des combinaisons de classes abstraites et d’interfaces pour optimiser vos compromis de conception.

Utilisez une classe abstraite

  • Lors de la création d’une bibliothèque de classes qui sera largement dissortingbuée ou réutilisée, en particulier pour les clients, utilisez une classe abstraite plutôt qu’une interface. car, cela simplifie la gestion des versions.

  • Utilisez une classe abstraite pour définir une classe de base commune pour une famille de types.

  • Utilisez une classe abstraite pour fournir un comportement par défaut.

  • Sous-classe uniquement une classe de base dans une hiérarchie à laquelle la classe appartient logiquement.

Utiliser une interface

  • Lors de la création d’un projet autonome pouvant être modifié à volonté, utilisez une interface plutôt qu’une classe abstraite. car il offre plus de flexibilité de conception.

  • Utilisez des interfaces pour introduire un comportement polymorphe sans sous-classement et pour modéliser plusieurs inheritances, ce qui permet à un type spécifique de prendre en charge de nombreux comportements.

  • Utilisez une interface pour concevoir une hiérarchie polymorphe pour les types de valeur.

  • Utilisez une interface lorsqu’un contrat immuable est vraiment voulu.

  • Une interface bien conçue définit une gamme de fonctionnalités très spécifique. Fractionner les interfaces qui contiennent des fonctionnalités non liées.

Vous pouvez implémenter un nombre illimité d’interfaces, mais vous ne pouvez hériter que d’ une classe. Ainsi, les classes et les interfaces sont des bêtes très différentes en C # et vous ne pouvez pas les utiliser de manière interchangeable. En C #, les classes abstraites sont toujours des classes et non des interfaces.

Les interfaces et les classes abstraites servent différents objectives. Les interfaces sont utilisées pour déclarer des contrats de classes, tandis que les classes abstraites permettent de partager une implémentation commune.

Si vous utilisez uniquement des classes abstraites, vos classes ne peuvent pas hériter d’autres classes, car C # ne prend pas en charge l’inheritance multiple. Si vous utilisez uniquement des interfaces, vos classes ne peuvent pas partager le code commun.

 public interface IFoo { void Bar(); } public abstract class FooBase : IFoo { public abstract void Bar() { // Do some stuff usually required for IFoo. } } 

Nous pouvons maintenant utiliser l’interface et l’implémentation de base dans différentes situations.

 public class FooOne : FooBase { public override void Bar() { base.Bar(); // Use base implementation. // Do specialized stuff. } } public class FooTwo : FooBase { public override void Bar() { // Do other specialized stuff. base.Bar(); // Use base implementation. // Do more specialized stuff. } } // This class cannot use the base implementation from FooBase because // of inheriting from OtherClass but it can still implement IFoo. public class FooThree : OtherClass, IFoo { public virtual void Bar() { // Do stuff. } } 

Si vous n’avez pas de code par défaut / commun, utilisez une interface.

Une classe abstraite peut également servir de modèle, dans lequel elle définit les étapes d’un algorithme et l’ordre dans lequel ils sont appelés. Les classes dérivées fournissent l’implémentation de ces étapes:

 public abstract class Processor { // this is the only public method // implements the order of the separate steps public void Process() { Step1(); Step2(); //... } // implementation is provided by derived classes protected abstract void Step1(); protected abstract void Step2(); } 

S’il est vrai qu’une classe abstraite sans implémentation est équivalente à une interface, les interfaces et les classes abstraites sont utilisées à des fins différentes.

Les interfaces peuvent être utilisées pour le polymorphism au sens le plus général. Par exemple, ICollection est utilisé pour définir l’interface pour toutes les collections (il en existe plusieurs). Ici, il s’agit de définir les opérations que vous souhaitez effectuer sur un certain type de type. Il existe de nombreuses autres utilisations (telles que la testabilité, l’dependency injection, etc.). En outre, les interfaces peuvent être mélangées et cela fonctionne à la fois conceptuellement et techniquement.

Les classes abstraites sont davantage liées au comportement modélisable, où les méthodes virtuelles sont un lieu pour «combler les lacunes». Évidemment, vous ne pouvez pas mélanger des classes abstraites (du moins, pas en C #).

En C #, un élément dissuasif important pour l’utilisation des classes abstraites est que vous ne pouvez en utiliser qu’une. Avec les interfaces, vous avez l’avantage de ne pas limiter la classe de base pour la mise en oeuvre. À cette fin, j’utilise toujours une interface même si je crée une classe de base abstraite pour faciliter l’implémentation.

Un autre inconvénient des classes abstraites de base est qu’elles ont tendance à s’appuyer sur des arguments de modèle. Cela peut rendre très difficile l’utilisation du rest de votre code. La réponse facile à cela est de fournir une interface permettant de parler à la classe abstraite sans connaître l’argument de type de la classe de modèle.

D’autres semblent taper leur réponse plus rapidement, mais permettez-moi de résumer …

Utilisez une interface. Si vous avez besoin de partager une implémentation, vous pouvez également créer une classe de base abstraite fournissant les détails de l’implémentation courants.

Notez qu’avec C # 3, vous pouvez fournir un comportement par défaut pour les interfaces via l’utilisation de méthodes d’extension. Il y a cependant quelques limitations et les classes abstraites ont toujours leur place.

La règle que je suis lors de la modélisation est la suivante: Classes (résumé inclus) et entités de modèle de struct. Comportement de modèle d’interfaces. Les entités implémentant une interface peuvent être considérées comme présentant des comportements que l’interface (contrat) expose.

Ceci est suggéré dans quelques réponses mais pas explicitement indiqué.

Le fait que vous puissiez implémenter plusieurs interfaces et n’hériter que d’une classe de base, comme si elles étaient les deux faces d’une même pièce, n’est pas une bonne façon de voir les choses.

Ne pensez pas que les interfaces font partie d’une hiérarchie d’objects. Ce ne sont généralement que de petites parties de fonctionnalités (ou du moins de spécificités, si ce n’est petites) que votre véritable object hiérarchique peut déclarer comme implémentant. Prenez IDisposable par exemple. Si vous écriviez cela, vous demanderiez-vous si cela aurait dû être une classe abstraite ou une interface? Il semble évident que, dans ce cas, il s’agit de deux choses complètement différentes. Je veux être jetable. Pensez ICloneable et IEnumerable. Vous pouvez les implémenter dans votre classe sans avoir à essayer de faire en sorte que votre classe dérive de classes non apparentées telles que List ou Array. Ou prenez IEnumerator. Donne simplement un type de vue MoveNext à un object. Ma classe peut fournir cette fonctionnalité sans devoir dériver maladroitement d’un autre type de données de collecte séquentielle qui n’a rien à voir avec ma classe.

Je préfère toujours les interfaces tant que la classe de base n’a pas d’implémentation vraiment “lourde” qui fera gagner beaucoup de temps aux développeurs. Donner que .net n’autorise qu’un inheritance de classe de base, forcer vos utilisateurs à hériter est une énorme limitation.

Vous devriez toujours préférer programmer aux interfaces qu’aux classes concrètes.

Si vous souhaitez également une implémentation par défaut, vous pouvez toujours créer une classe de base qui implémente votre ou vos interfaces.