Il peut s’agir d’une question générique OOP. Je voulais faire une comparaison générique entre une interface et une classe abstraite sur la base de leur utilisation.
Quand voudrait-on utiliser une interface et quand voudrait-on utiliser une classe abstraite?
J'ai écrit un article à ce sujet:
Classes abstraites et interfaces
Résumant:
Lorsque nous parlons de classes abstraites, nous définissons les caractéristiques d’un type d’objet; en spécifiant ce qu'est un objet.
Lorsque nous parlons d’une interface et définissons les fonctionnalités que nous nous engageons à fournir, nous parlons d’établir un contrat sur ce que l’objet peut faire.
Une classe abstraite peut avoir un état ou une fonctionnalité partagée. Une interface n'est qu'une promesse de fournir l'état ou la fonctionnalité. Une bonne classe abstraite réduira la quantité de code à réécrire car sa fonctionnalité ou son état peut être partagé. L'interface n'a aucune information définie à partager
Personnellement, je n'ai presque jamais besoin d'écrire des classes abstraites.
La plupart du temps, je vois que des classes abstraites sont (mal) utilisées, c'est parce que l'auteur de la classe abstraite utilise le modèle "Template method".
Le problème avec la "méthode Template" est qu’elle est presque toujours quelque peu réentrante - la classe "dérivée" connaît non seulement la méthode "abstraite" de sa classe de base qu’elle implémente, mais aussi les méthodes publiques de la classe de base. , même si la plupart du temps, il n’est pas nécessaire de les appeler.
Exemple (trop simplifié):
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
Donc, ici, l'auteur de cette classe a écrit un algorithme générique et a l'intention de l'utiliser en le "spécialisant" en fournissant ses propres "crochets" - dans ce cas, une méthode de "comparaison".
Donc, l'utilisation prévue ressemble à ceci:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
Le problème, c’est que vous avez indûment associé deux concepts:
Dans le code ci-dessus, théoriquement, l'auteur de la méthode "compare" peut ré-entrerly rappeler dans la méthode "Trier" de la superclasse ... même si, dans la pratique, il ne voudra jamais ou n'aura jamais besoin de le faire.
Le prix que vous payez pour ce couplage inutile est qu’il est difficile de changer la super-classe et, dans la plupart des langues OO, impossible de le changer au moment de l’exécution.
La méthode alternative consiste à utiliser le modèle de conception "Stratégie" à la place:
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
Avis donc maintenant: tout ce que nous avons sont des interfaces et des implémentations concrètes de ces interfaces. En pratique, vous n'avez besoin de rien d'autre pour réaliser un design de haut niveau OO.
Pour "masquer" le fait que nous ayons implémenté le "tri des noms" en utilisant une classe "QuickSort" et un "NameComparator", nous pourrions toujours écrire une méthode factory quelque part:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
N'importe quel fois que vous avez une classe abstraite, vous pouvez le faire ... même s'il existe une relation de ré-entrante naturelle entre la classe de base et la classe dérivée, il est généralement rentable de les rendre explicites.
Une dernière pensée: Tout ce que nous avons fait ci-dessus est de "composer" une fonction "NameSorting" en utilisant une fonction "QuickSort" et une fonction "NameComparison" ... dans un langage de programmation fonctionnel, ce style de programmation devient encore plus naturel, avec moins de code.
OK, je viens juste de "grokked" ceci - ici c'est en termes simples (n'hésitez pas à me corriger si je me trompe) - Je sais que ce sujet est oooooold, mais quelqu'un d'autre pourrait tomber par hasard sur lui un jour ...
Les classes abstraites vous permettent de créer un plan directeur et vous permettent également d'ajouter les propriétés et méthodes CONSTRUCT (implémentation) que vous souhaitez que TOUS ses descendants possèdent.
En revanche, une interface ne vous permet que de déclarer que vous souhaitez que des propriétés et/ou des méthodes portant un nom donné existent dans toutes les classes qui l’implémentent, sans toutefois préciser comment l’appliquer. De plus, une classe peut implémenter de nombreuses interfaces, mais ne peut étendre qu'une classe abstraite. Une interface est davantage un outil architectural de haut niveau (qui devient plus claire si vous commencez à comprendre les modèles de conception): un résumé a un pied dans les deux camps et peut également effectuer une partie du travail compliqué.
Pourquoi utiliser l'un sur l'autre? Le premier permet une définition plus concrète des descendants - le dernier permet un plus grand polymorphisme. Ce dernier point est important pour l'utilisateur final/le codeur, qui peut utiliser ces informations pour implémenter l'AP. I(nterface) dans une variété de combinaisons/formes en fonction de leurs besoins.
Je pense que c’était le moment décisif pour moi. Réfléchissez aux interfaces moins du point de vue de l’auteur et davantage à celles de tous les codeurs qui viendront plus tard dans la chaîne et qui ajoutent une implémentation à un projet ou étendent une API.
Mes deux centimes:
Une interface définit essentiellement un contrat auquel toute classe d'implémentation doit adhérer (implémenter les membres de l'interface). Il ne contient aucun code.
D'autre part, une classe abstraite peut contenir du code et il peut y avoir des méthodes marquées comme abstraites qu'une classe héritante doit implémenter.
Les rares situations dans lesquelles j'ai utilisé des classes abstraites, c’est lorsque j’ai une fonctionnalité par défaut que la classe héritante n’a peut-être pas intérêt à remplacer, par exemple une classe de base abstraite, dont certaines classes spécialisées héritent.
Exemple (très rudimentaire!): Prenons une classe de base appelée Client qui comporte des méthodes abstraites telles que CalculatePayment()
, CalculateRewardPoints()
et des méthodes non abstraites telles que GetName()
, SavePaymentDetails()
.
Les classes spécialisées telles que RegularCustomer
et GoldCustomer
hériteront de la classe de base Customer
et implémenteront leur propre logique de méthode CalculatePayment()
et CalculateRewardPoints()
, mais réutiliseront les méthodes GetName()
et SavePaymentDetails()
.
Vous pouvez ajouter plus de fonctionnalités à une classe abstraite (c'est-à-dire des méthodes non abstraites) sans affecter les classes enfants qui utilisaient une version plus ancienne. Considérant que l’ajout de méthodes à une interface affecterait toutes les classes qui l’implémenteraient, car elles auraient maintenant besoin de mettre en œuvre les membres d’interface nouvellement ajoutés.
Une classe abstraite avec tous les membres abstraits serait similaire à une interface.
Quand faire ce qui est une chose très simple si le concept est clair dans votre esprit.
Les classes abstraites peuvent être dérivées alors que les interfaces peuvent être implémentées. Il y a une différence entre les deux. Lorsque vous dérivez une classe abstraite, la relation entre la classe dérivée et la classe de base est "est une" relation. Par exemple, un chien est un animal, un mouton est un animal, ce qui signifie qu'une classe dérivée hérite de propriétés de la classe de base.
Alors que pour la mise en œuvre des interfaces, la relation est "peut être". Par exemple, un chien peut être un chien espion. Un chien peut être un chien de cirque. Un chien peut être un chien de race. Ce qui signifie que vous implémentez certaines méthodes pour acquérir quelque chose.
J'espère que je suis clair.
Si vous regardez Java comme langage OOP,
"l'interface ne fournit pas l'implémentation de méthode}" n'est plus valide avec le lancement de Java 8. Maintenant, Java fournit une implémentation dans l'interface pour les méthodes par défaut.
En termes simples, je voudrais utiliser
interface: _ Pour implémenter un contrat avec plusieurs objets non liés. Il fournit la capacité "HAS A".
classe abstraite: _ Pour appliquer le même comportement ou un comportement différent entre plusieurs objets liés. Il établit une relation "IS A".
Oracle website fournit les principales différences entre les classes interface
et abstract
.
Pensez à utiliser des classes abstraites si:
Envisagez d'utiliser des interfaces si:
Serializable
.Exemple:
Classe abstraite (relation IS A)
Reader est une classe abstraite.
BufferedReader est une Reader
FileReader est une Reader
FileReader
et BufferedReader
sont utilisés à des fins communes: lecture de données, et ils sont liés par le biais de la classe Reader
.
Interface (capacité HAS A)
Serializable est une interface.
Supposons que vous ayez deux classes dans votre application, qui implémentent l'interface Serializable
Employee implements Serializable
Game implements Serializable
Ici, vous ne pouvez pas établir de relation via l'interface Serializable
entre Employee
et Game
, qui ont un but différent. Les deux sont capables de sérialiser l'état et la comparaison se termine là.
Jetez un coup d'œil à ces articles:
Comment aurais-je dû expliquer la différence entre une classe d'interface et une classe abstraite?
J'ai écrit un article sur l'utilisation des classes abstraites et des interfaces. Il y a beaucoup plus de différence entre eux autre que "un IS-A ... et un CAN-DO ...". Pour moi, ce sont des réponses en conserve. Je mentionne quelques raisons d'utiliser l'un ou l'autre. J'espère que ça aide.
1.Si vous créez quelque chose qui fournit des fonctionnalités communes à des classes non liées, utilisez une interface.
2.Si vous créez quelque chose pour des objets étroitement liés dans une hiérarchie, utilisez une classe abstraite.
Je pense que la manière la plus succincte de la formuler est la suivante:
Propriétés partagées => classe abstraite.
Fonctionnalité partagée => interface.
Et pour le dire moins succinctement ...
Exemple de classe abstraite:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
Étant donné que les animaux ont une propriété partagée - le nombre de pattes dans ce cas - il est logique de créer une classe abstraite contenant cette propriété partagée. Cela nous permet également d’écrire un code commun qui fonctionne sur cette propriété. Par exemple:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
Exemple d'interface:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
Notez ici que Vuvuzelas et Cars sont des choses complètement différentes, mais elles ont une fonctionnalité commune: faire un son. Ainsi, une interface a un sens ici. De plus, cela permettra aux programmeurs de regrouper les éléments qui produisent des sons dans une interface commune - IMakeSound
dans ce cas. Avec cette conception, vous pourriez écrire le code suivant:
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
Pouvez-vous dire ce que cela produirait?
Enfin, vous pouvez combiner les deux.
Exemple combiné:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
Ici, nous demandons à tous les BaseAnimal
s de créer un son, mais nous ne connaissons pas encore son implémentation. Dans un tel cas, nous pouvons résumer l'implémentation de l'interface et déléguer celle-ci à ses sous-classes.
Un dernier point, rappelez-vous comment, dans l'exemple de classe abstraite, nous avons pu utiliser les propriétés partagées de différents objets et dans l'exemple d'interface, nous avons pu appeler la fonctionnalité partagée de différents objets? Dans ce dernier exemple, nous pourrions faire les deux.
Pensez à utiliser classes abstraites si l'une de ces déclarations s'applique à votre situation:
Pensez à utiliser interfaces si l'une de ces déclarations s'applique à votre situation:
Les classes peuvent hériter d'une seule classe de base. Par conséquent, si vous souhaitez utiliser des classes abstraites pour fournir un polymorphisme à un groupe de classes, elles doivent toutes hériter de cette classe. Les classes abstraites peuvent également fournir des membres qui ont déjà été implémentés. Par conséquent, vous pouvez assurer un certain nombre de fonctionnalités identiques avec une classe abstraite, mais pas avec une interface.
Voici quelques recommandations pour vous aider à décider si vous souhaitez utiliser une interface ou une classe abstraite pour fournir un polymorphisme à vos composants.
Copié de:
http://msdn.Microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Pour moi, je choisirais souvent des interfaces. Mais je préfère les classes abstraites dans certains cas.
Les classes dans OO font généralement référence à la mise en œuvre. J'utilise des classes abstraites lorsque je veux imposer certains détails d'implémentation aux enfants, sinon j'utilise des interfaces.
Bien entendu, les classes abstraites sont utiles non seulement pour forcer la mise en œuvre, mais également pour partager certains détails spécifiques entre de nombreuses classes liées.
Les réponses varient selon les langues. Par exemple, en Java, une classe peut implémenter (hériter de) plusieurs interfaces mais seulement hériter d'une classe abstraite. Les interfaces vous donnent donc plus de flexibilité. Mais ce n'est pas vrai en C++.
Utilisez une classe abstraite si vous souhaitez fournir des implémentations de base.
en Java, vous pouvez hériter d'une classe (abstraite) pour "fournir" la fonctionnalité et vous pouvez implémenter de nombreuses interfaces pour "assurer" la fonctionnalité
Un endroit intéressant où les interfaces se comportent mieux que les classes abstraites est lorsque vous devez ajouter des fonctionnalités supplémentaires à un groupe d'objets (liés ou non liés). Si vous ne pouvez pas leur donner une classe abstraite de base (par exemple, ils sont sealed
ou déjà un parent), vous pouvez leur donner une interface fictive (vide), puis simplement écrire des méthodes d'extension pour cette interface.
Purely sur la base de l'héritage, vous utiliseriez un résumé où vous définissez clairement les relations descendantes, abstraites (c'est-à-dire animal-> chat) et/ou exigez l'héritage de propriétés virtuelles ou non publiques, en particulier d'état partagé ( que les interfaces ne peuvent pas supporter).
Vous devriez essayer de privilégier la composition (via l'injection de dépendance) plutôt que l'héritage, et notez que les interfaces en tant que contrats prennent en charge les tests unitaires, la séparation des problèmes et l'héritage multiple (variable dans la langue) d'une manière que les abstraits ne peuvent pas.
Quand préférer une classe abstraite à une interface?
Quand préférer une interface à une classe abstraite?
Cela peut être un appel très difficile à faire ...
Un point que je peux donner: un objet peut implémenter de nombreuses interfaces, alors qu’un objet ne peut hériter que d’une classe de base (dans un langage OO moderne comme c #, je sais que le C++ a un héritage multiple - mais n’est-ce pas mal vu? )
Une classe abstraite peut avoir des implémentations.
Une interface n'a pas d'implémentations, elle définit simplement une sorte de contrat.
Il peut également y avoir certaines différences dépendant de la langue: par exemple, C # ne possède pas plusieurs héritages, mais plusieurs interfaces peuvent être implémentées dans une classe.