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!