web-dev-qa-db-fra.com

Quand utiliser une interface au lieu d'une classe abstraite et vice versa?

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?

374
Chirantan

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.

373
Jorge Córdoba

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

394
Alex

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:

  1. Une façon de comparer deux éléments (quel élément doit commencer en premier)
  2. Une méthode de tri des éléments (c.-à-d. Tri rapide, tri par fusion, etc.)

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.

75
Paul Hollingsworth

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.

38
sunwukung

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.

35
PreethaA

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.

29
Aamir

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:

  1. Vous souhaitez partager du code entre plusieurs classes étroitement liées.
  2. Vous vous attendez à ce que les classes qui étendent votre classe abstraite aient de nombreuses méthodes ou champs communs, ou nécessitent des modificateurs d'accès autres que public (tels que protected et private).
  3. Vous voulez déclarer des champs non statiques ou non finaux. 

Envisagez d'utiliser des interfaces si:

  1. Vous vous attendez à ce que des classes non liées implémentent votre interface. Par exemple, de nombreux objets non liés peuvent implémenter l'interface Serializable.
  2. Vous souhaitez spécifier le comportement d'un type de données particulier, sans vous soucier de savoir qui l'implémente.
  3. Vous souhaitez tirer parti de l'héritage multiple de type.

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?

27
Ravindra babu

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.

http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abstract-class-and-an-interface/

10
marcel

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.

10
kuttychutty

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 BaseAnimals 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. 

4
Domn Werner

Pensez à utiliser classes abstraites si l'une de ces déclarations s'applique à votre situation:

  1. Vous souhaitez partager du code entre plusieurs classes étroitement liées.
  2. Vous vous attendez à ce que les classes qui étendent votre classe abstraite aient de nombreuses méthodes ou champs communs ou nécessitent des modificateurs d'accès autres que public (comme protégé et privé).
  3. Vous voulez déclarer des champs non statiques ou non finaux. Cela vous permet de définir des méthodes pouvant accéder à et modifier l'état de l'objet auquel elles appartiennent.

Pensez à utiliser interfaces si l'une de ces déclarations s'applique à votre situation:

  1. Vous vous attendez à ce que des classes non liées implémentent votre interface. Par exemple, les interfaces Comparable et Cloneable sont implémentées par de nombreuses classes non liées.
  2. Vous souhaitez spécifier le comportement d'un type de données particulier, sans vous soucier de savoir qui l'implémente.
  3. Vous souhaitez tirer parti de plusieurs héritages.

La source

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.

  • Si vous envisagez de créer plusieurs versions de votre composant, créez une classe abstraite. Les classes abstraites offrent un moyen simple et facile de mettre à jour vos composants. En mettant à jour la classe de base, toutes les classes héritées sont automatiquement mises à jour avec la modification. Les interfaces, en revanche, ne peuvent pas être modifiées une fois créées de cette manière. Si une nouvelle version d'une interface est requise, vous devez créer une toute nouvelle interface . 
  • Si la fonctionnalité que vous créez sera utile pour une large gamme d'objets disparates, utilisez une interface. Les classes abstraites doivent être utilisées principalement pour les objets étroitement liés, tandis que les interfaces conviennent mieux à la fourniture de fonctionnalités communes à des classes non liées . 
  • Si vous concevez de petites fonctionnalités concises, utilisez des interfaces. Si vous concevez de grandes unités fonctionnelles, utilisez une classe abstraite . 
  • Si vous souhaitez fournir des fonctionnalités communes et implémentées parmi toutes les implémentations de votre composant, utilisez une classe abstraite. Les classes abstraites vous permettent d'implémenter partiellement votre classe, alors que les interfaces ne contiennent aucune implémentation pour les membres.

Copié de:
http://msdn.Microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx

3
Mazhar

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.

2
La VloZ Merrill

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++.

2
Nick Fortescue

Utilisez une classe abstraite si vous souhaitez fournir des implémentations de base.

1
Sebastian

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é

1
Peter Miehle

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.

1
Dmitri Nesteruk

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.

1
annakata

Quand préférer une classe abstraite à une interface?

  1. Si l'on envisage de mettre à jour une classe de base tout au long de la vie d'un programme/projet, il est préférable d'autoriser la classe de base à être une classe abstraite.
  2. Si vous essayez de créer un réseau fédérateur pour des objets étroitement liés dans une hiérarchie, il est très avantageux d’utiliser une classe abstraite.

Quand préférer une interface à une classe abstraite?

  1. Si l'on ne traite pas avec un type de cadre hiérarchique massif, les interfaces seraient un excellent choix
  2. Étant donné que l'héritage multiple n'est pas pris en charge avec les classes abstraites (problème de diamant), les interfaces peuvent faire gagner du temps. 
1
Satya

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? )

0
Mesh

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.

0
Gerrie Schenck