[Oop] Interface vs classe de base


Answers

Prenons votre exemple d'une classe Chien et Cat, et illustrons en utilisant C #:

Le chien et le chat sont tous deux des animaux, en particulier des mammifères quadrupèdes (les animaux sont trop généraux). Supposons que vous ayez une classe Mammale abstraite, pour les deux:

public abstract class Mammal

Cette classe de base aura probablement des méthodes par défaut telles que:

  • Alimentation
  • Camarade

Tous sont des comportements qui ont plus ou moins la même mise en œuvre entre les deux espèces. Pour définir ceci, vous aurez:

public class Dog : Mammal
public class Cat : Mammal

Supposons maintenant qu'il y a d'autres mammifères, que nous verrons habituellement dans un zoo:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Cela sera toujours valide car au cœur de la fonctionnalité Feed() et Mate() seront toujours les mêmes.

Cependant, les girafes, les rhinocéros et les hippopotames ne sont pas exactement des animaux dont vous pouvez faire des animaux de compagnie. C'est là qu'une interface sera utile:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

La mise en œuvre pour le contrat ci-dessus ne sera pas la même entre un chat et un chien; mettre leurs implémentations dans une classe abstraite à hériter sera une mauvaise idée.

Vos définitions de chien et de chat devraient maintenant ressembler à:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Théoriquement, vous pouvez les remplacer à partir d'une classe de base supérieure, mais essentiellement une interface vous permet d'ajouter uniquement les choses dont vous avez besoin dans une classe sans avoir besoin d'héritage.

Par conséquent, comme vous ne pouvez généralement hériter que d'une classe abstraite (dans la plupart des langages OO statiquement typés qui sont ... exceptions incluent C ++) mais être capable d'implémenter plusieurs interfaces, cela vous permet de construire des objets strictement selon les besoins .

Question

Quand dois-je utiliser une interface et quand dois-je utiliser une classe de base?

Devrait-il toujours être une interface si je ne veux pas réellement définir une implémentation de base des méthodes?

Si j'ai un cours de chien et chat. Pourquoi voudrais-je implémenter IPet au lieu de PetBase? Je peux comprendre avoir des interfaces pour ISheds ou IBarks (IMakesNoise?), Parce que ceux-ci peuvent être placés sur un animal de compagnie par animal de compagnie, mais je ne comprends pas lequel utiliser pour un animal de compagnie générique.




Interfaces

  • Définit le contrat entre 2 modules. Ne peut avoir aucune implémentation.
  • La plupart des langues vous permettent d'implémenter plusieurs interfaces
  • Modifier une interface est un changement de rupture. Toutes les implémentations doivent être recompilées / modifiées.
  • Tous les membres sont publics. Les implémentations doivent implémenter tous les membres.
  • Les interfaces aident au découplage. Vous pouvez utiliser des cadres fictifs pour simuler quoi que ce soit derrière une interface
  • Les interfaces indiquent normalement une sorte de comportement
  • Les implémentations d'interface sont découplées / isolées les unes des autres

Classes de base

  • Vous permet d'ajouter une implémentation par défaut que vous obtenez gratuitement par dérivation
  • Sauf C ++, vous ne pouvez dériver que d'une classe. Même si cela peut être de plusieurs classes, c'est généralement une mauvaise idée.
  • Changer la classe de base est relativement facile. Les dérivations n'ont pas besoin de faire quelque chose de spécial
  • Les classes de base peuvent déclarer des fonctions protégées et publiques accessibles par des dérivations
  • Résumé Les classes de base ne peuvent pas être facilement manipulées comme des interfaces
  • Les classes de base indiquent normalement la hiérarchie de type (IS A)
  • Les dérivations de classe peuvent dépendre d'un comportement de base (avoir une connaissance complexe de l'implémentation parente). Les choses peuvent être désordonnées si vous faites un changement à la mise en œuvre de base pour un gars et briser les autres.



Les interfaces et les classes de base représentent deux formes différentes de relations.

L'héritage (classes de base) représente une relation "est-a". Par exemple un chien ou un chat "est-un" animal de compagnie. Cette relation représente toujours le but (unique) de la classe (en conjonction avec le «principe de responsabilité unique» ).

Les interfaces , d'un autre côté, représentent des fonctionnalités supplémentaires d'une classe. Je l'appellerais une relation "est", comme dans " Foo est jetable", d'où l'interface IDisposable en C #.




Quand j'ai commencé à apprendre sur la programmation orientée objet, j'ai fait l'erreur facile et probablement commune d'utiliser l'héritage pour partager un comportement commun - même lorsque ce comportement n'était pas essentiel à la nature de l'objet.

Pour construire plus loin un exemple très utilisé dans cette question particulière, il y a beaucoup de choses qui sont petable - copines, voitures, couvertures floues ... - donc j'ai pu avoir une classe Petable qui a fourni ce comportement commun, et diverses classes héritant à partir de cela.

Cependant, être apte au pétrissage ne fait pas partie de la nature de ces objets. Il y a des concepts beaucoup plus importants qui sont essentiels à leur nature - la petite amie est une personne, la voiture est un véhicule terrestre, le chat est un mammifère ...

Les comportements doivent être assignés d'abord aux interfaces (y compris l'interface par défaut de la classe), et promus à une classe de base seulement s'ils sont (a) communs à un grand groupe de classes qui sont des sous-ensembles d'une classe plus grande - dans le même sens que "chat" et "personne" sont des sous-ensembles de "mammifère".

Le hic, c'est qu'après avoir compris la conception orientée objet de manière suffisamment meilleure que je l'ai fait au début, vous le ferez normalement automatiquement sans même y penser. Ainsi, la simple vérité de la déclaration «code à une interface, pas une classe abstraite» devient si évidente que vous avez du mal à croire que quelqu'un prend la peine de le dire - et commencez à essayer de lire d'autres significations.

Une autre chose que j'ajouterais est que si une classe est purement abstraite - sans aucun membre non abstrait, non hérité ou des méthodes exposées à l'enfant, parent, ou client - alors pourquoi est-ce une classe? Il pourrait être remplacé, dans certains cas, par une interface et dans d'autres cas par Null.




C'est assez .NET spécifique, mais le livre de directives de conception de cadre soutient que les classes générales donnent plus de flexibilité dans un cadre évolutif. Une fois qu'une interface est expédiée, vous n'avez pas la possibilité de la changer sans casser le code qui a utilisé cette interface. Avec une classe cependant, vous pouvez le modifier et ne pas casser le code qui y est lié. Tant que vous faites les bonnes modifications, ce qui inclut l'ajout de nouvelles fonctionnalités, vous serez en mesure d'étendre et de faire évoluer votre code.

Krzysztof Cwalina dit à la page 81:

Au cours des trois versions de .NET Framework, j'ai parlé de cette directive avec quelques développeurs de notre équipe. Beaucoup d'entre eux, y compris ceux qui étaient initialement en désaccord avec les lignes directrices, ont dit qu'ils regrettent d'avoir livré une interface API. Je n'ai jamais entendu parler d'un seul cas où quelqu'un aurait regretté l'envoi d'un cours.

Cela étant dit, il y a certainement une place pour les interfaces. En règle générale, fournissez toujours une implémentation abstraite de la classe de base d'une interface si ce n'est pour rien d'autre comme un exemple d'implémentation de l'interface. Dans le meilleur des cas, cette classe de base permettra d'économiser beaucoup de travail.




Le style moderne est de définir IPet et PetBase.

L'avantage de l'interface est que l'autre code peut l'utiliser sans aucun lien avec un autre code exécutable. Complètement "propre". Les interfaces peuvent également être mélangées.

Mais les classes de base sont utiles pour les implémentations simples et les utilitaires communs. Donc fournissez aussi une classe de base abstraite pour gagner du temps et du code.




I usually don't implement either until I need one. I favor interfaces over abstract classes because that gives a little more flexibility. If there's common behavior in some of the inheriting classes I move that up and make an abstract base class. I don't see the need for both, since they essentially server the same purpose, and having both is a bad code smell (imho) that the solution has been over-engineered.




It depends on your requirements. If IPet is simple enough, I would prefer to implement that. Otherwise, if PetBase implements a ton of functionality you don't want to duplicate, then have at it.

The downside to implementing a base class is the requirement to override (or new ) existing methods. This makes them virtual methods which means you have to be careful about how you use the object instance.

Lastly, the single inheritance of .NET kills me. A naive example: Say you're making a user control, so you inherit UserControl . But, now you're locked out of also inheriting PetBase . This forces you to reorganize, such as to make a PetBase class member, instead.




Une différence importante est que vous ne pouvez hériter que d' une classe de base, mais vous pouvez implémenter de nombreuses interfaces. Vous ne voulez donc utiliser qu'une classe de base si vous êtes absolument certain que vous n'aurez pas besoin d'hériter d'une classe de base différente. De plus, si vous trouvez que votre interface est grande, vous devriez commencer à la décomposer en quelques parties logiques qui définissent des fonctionnalités indépendantes, car il n'y a aucune règle que votre classe ne puisse les implémenter toutes (ou que vous pouvez définir une autre interface qui hérite de tous pour les regrouper).




Je recommande d'utiliser la composition au lieu de l'héritage autant que possible. Utilisez des interfaces, mais utilisez des objets membres pour l'implémentation de base. De cette façon, vous pouvez définir une fabrique qui construit vos objets de manière à se comporter d'une certaine manière. Si vous souhaitez modifier le comportement, vous créez une nouvelle méthode d'usine (ou fabrique abstraite) qui crée différents types de sous-objets.

Dans certains cas, vous pouvez constater que vos objets principaux n'ont pas besoin d'interfaces du tout, si tout le comportement mutable est défini dans les objets auxiliaires.

Ainsi, au lieu de IPet ou PetBase, vous pourriez vous retrouver avec un animal de compagnie qui a un paramètre IFurBehavior. Le paramètre IFurBehavior est défini par la méthode CreateDog () de PetFactory. C'est ce paramètre qui est appelé pour la méthode shed ().

Si vous faites cela, vous verrez que votre code est beaucoup plus flexible et la plupart de vos objets simples traitent de comportements très basiques à l'échelle du système.

Je recommande ce modèle même dans les langues à plusieurs héritages.




Use Interfaces to enforce a contract ACROSS families of unrelated classes. For example, you might have common access methods for classes that represent collections, but contain radically different data ie one class might represent a result set from a query, while the other might represent the images in a gallery. Also, you can implement multiple interfaces, thus allowing you to blend (and signify) the capabilities of the class.

Use Inheritance lorsque les classes portent une relation commune et ont donc une signature structurelle et comportementale similaire, c'est-à-dire Car, Moto, Camion et SUV sont tous les types de véhicules routiers qui peuvent contenir un certain nombre de roues, une vitesse de pointe




Le cas des classes de base sur les interfaces a été bien expliqué dans les directives de codage .NET sous-marines:

Classes de base et interfaces Un type d'interface est une description partielle d'une valeur potentiellement prise en charge par de nombreux types d'objets. Utilisez des classes de base au lieu d'interfaces autant que possible. Du point de vue de la gestion des versions, les classes sont plus flexibles que les interfaces. Avec une classe, vous pouvez expédier la version 1.0 puis, dans la version 2.0, ajouter une nouvelle méthode à la classe. Tant que la méthode n'est pas abstraite, les classes dérivées existantes continuent à fonctionner sans changement.

Les interfaces ne prenant pas en charge l'héritage d'implémentation, le modèle qui s'applique aux classes ne s'applique pas aux interfaces. Ajouter une méthode à une interface équivaut à ajouter une méthode abstraite à une classe de base; toute classe qui implémente l'interface va se casser car la classe n'implémente pas la nouvelle méthode. Les interfaces sont appropriées dans les situations suivantes:

  1. Plusieurs classes non apparentées veulent supporter le protocole.
  2. Ces classes ont déjà des classes de base établies (par exemple, certaines sont des contrôles d'interface utilisateur (UI) et certains sont des services Web XML).
  3. L'agrégation n'est pas appropriée ou praticable. Dans toutes les autres situations, l'héritage de classe est un meilleur modèle.



Gardez aussi à l'esprit de ne pas vous perdre dans OO ( voir blog ) et modélisez toujours les objets en fonction du comportement requis, si vous concevez une application dont le seul comportement requis est un nom générique et une espèce pour un animal, alors vous aurez seulement besoin Un animal de classe avec une propriété pour le nom, au lieu de millions de classes pour chaque animal possible dans le monde.




N'utilisez pas une classe de base à moins de savoir ce que cela signifie et qu'elle s'applique dans ce cas. Si cela s'applique, utilisez-le, sinon, utilisez des interfaces. Mais notez la réponse à propos des petites interfaces.

L'héritage public est surutilisé dans OOD et exprime beaucoup plus que ce que la plupart des développeurs réalisent ou sont prêts à respecter. Voir le principe de substituabilité de Liskov

En résumé, si A "est un" B alors A n'exige pas plus de B et ne livre pas moins de B, pour chaque méthode qu'il expose.




Juan,

J'aime penser aux interfaces comme un moyen de caractériser une classe. Une classe de race de chien particulière, disons un YorkshireTerrier, peut être une descendante de la classe de chien parent, mais elle implémente aussi IFurry, IStubby, et IYippieDog. Donc la classe définit ce qu'est la classe mais l'interface nous en dit des choses.

L'avantage est que cela me permet, par exemple, de rassembler tous les IYippieDog et de les jeter dans ma collection Ocean. Alors maintenant je peux atteindre un ensemble d'objets particulier et trouver ceux qui répondent aux critères que je regarde sans trop inspecter la classe.

Je trouve que les interfaces devraient vraiment définir un sous-ensemble du comportement public d'une classe. Si elle définit tout le comportement public pour toutes les classes qui l'implémentent, elle n'a généralement pas besoin d'exister. Ils ne me disent rien d'utile.

Cette pensée va cependant à l'encontre de l'idée que chaque classe devrait avoir une interface et que vous devriez coder à l'interface. C'est bien, mais vous vous retrouvez avec beaucoup d'interfaces individuelles vers les classes et cela rend les choses confuses. Je comprends que l'idée est que cela ne coûte vraiment rien à faire et maintenant vous pouvez échanger des choses avec facilité. Cependant, je trouve que je le fais rarement. La plupart du temps, je ne fais que modifier la classe existante et avoir exactement les mêmes problèmes que ceux que j'ai toujours rencontrés si l'interface publique de cette classe doit changer, sauf que je dois maintenant la changer à deux endroits.

Donc, si vous pensez comme moi, vous direz certainement que chat et chien sont IPettable. C'est une caractérisation qui les associe tous les deux.

L'autre partie de ceci est cependant devrait-ils avoir la même classe de base? La question est de savoir s'ils doivent être traités de la même manière. Certes, ils sont tous deux des animaux, mais cela correspond-il à la façon dont nous allons les utiliser ensemble.

Dites que je veux rassembler toutes les classes d'animaux et les mettre dans mon récipient Ark.

Ou ont-ils besoin d'être des mammifères? Peut-être avons-nous besoin d'une sorte d'usine de traite des animaux?

Ont-ils même besoin d'être liés ensemble? Est-ce suffisant de savoir qu'ils sont tous les deux IPettable?

Je ressens souvent le désir de dériver une hiérarchie de classe entière quand j'ai vraiment juste besoin d'une classe. Je le fais en anticipation un jour je pourrais en avoir besoin et d'habitude je ne le fais jamais. Même quand je le fais, je trouve habituellement que je dois faire beaucoup pour le réparer. C'est parce que le premier cours que je crée n'est pas le chien, je ne suis pas chanceux, mais plutôt l'ornithorynque. Maintenant, toute ma hiérarchie de classe est basée sur le cas bizarre et j'ai beaucoup de code gaspillé.

Vous pouvez également trouver à un certain point que tous les chats ne sont pas IPettable (comme celui glabre). Vous pouvez maintenant déplacer cette interface vers toutes les classes dérivées qui correspondent. Vous constaterez qu'un changement beaucoup moins brusque que tout à coup les chats ne sont plus dérivés de PettableBase.




An inheritor of a base class should have an "is a" relationship. Interface represents An "implements a" relationship. So only use a base class when your inheritors will maintain the is a relationship.




Source : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C# is a wonderful language that has matured and evolved over the last 14 years. This is great for us developers because a mature language provides us with a plethora of language features that are at our disposal.

However, with much power becomes much responsibility. Some of these features can be misused, or sometimes it is hard to understand why you would choose to use one feature over another. Over the years, a feature that I have seen many developers struggle with is when to choose to use an interface or to choose to use an abstract class. Both have there advantages and disadvantages and the correct time and place to use each. But how to we decide???

Both provide for reuse of common functionality between types. The most obvious difference right away is that interfaces provide no implementation for their functionality whereas abstract classes allow you to implement some “base” or “default” behavior and then have the ability to “override” this default behavior with the classes derived types if necessary.

This is all well and good and provides for great reuse of code and adheres to the DRY (Don't Repeat Yourself) principle of software development. Abstract classes are great to use when you have an “is a” relationship.

For example: A golden retriever “is a” type of dog. So is a poodle. They both can bark, as all dogs can. However, you might want to state that the poodle park is significantly different than the “default” dog bark. Therefor, it could make sense for you to implement something as follows:

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

// 

As you can see, this would be a great way to keep your code DRY and allow for the base class implementation be called when any of the types can just rely on the default Bark instead of a special case implementation. The classes like GoldenRetriever, Boxer, Lab could all could inherit the “default” (bass class) Bark at no charge just because they implement the Dog abstract class.

But I'm sure you already knew that.

You are here because you want to understand why you might want to choose an interface over an abstract class or vice versa. Well one reason you may want to choose an interface over an abstract class is when you don't have or want to prevent a default implementation. This is usually because the types that are implementing the interface not related in an “is a” relationship. Actually, they don't have to be related at all except for the fact that each type “is able” or has “the ablity” to do something or have something.

Now what the heck does that mean? Well, for example: A human is not a duck…and a duck is not a human. Pretty obvious. However, both a duck and a human have “the ability” to swim (given that the human passed his swimming lessons in 1st grade :) ). Also, since a duck is not a human or vice versa, this is not an “is a” realationship, but instead an “is able” relationship and we can use an interface to illustrate that:

// 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!

Using interfaces like the code above will allow you to pass an object into a method that “is able” to do something. The code doesn't care how it does it…All it knows is that it can call the Swim method on that object and that object will know which behavior take at run-time based on its type.

Once again, this helps your code stay DRY so that you would not have to write multiple methods that are calling the object to preform the same core function (ShowHowHumanSwims(human), ShowHowDuckSwims(duck), etc.)

Using an interface here allows the calling methods to not have to worry about what type is which or how the behavior is implemented. It just knows that given the interface, each object will have to have implemented the Swim method so it is safe to call it in its own code and allow the behavior of the Swim method be handled within its own class.

Summary:

So my main rule of thumb is use an abstract class when you want to implement a “default” functionality for a class hierarchy or/and the classes or types you are working with share a “is a” relationship (ex. poodle “is a” type of dog).

On the other hand use an interface when you do not have an “is a” relationship but have types that share “the ability” to do something or have something (ex. Duck “is not” a human. However, duck and human share “the ability” to swim).

Another difference to note between abstract classes and interfaces is that a class can implement one to many interfaces but a class can only inherit from ONE abstract class (or any class for that matter). Yes, you can nest classes and have an inheritance hierarchy (which many programs do and should have) but you cannot inherit two classes in one derived class definition (this rule applies to C#. In some other languages you are able to do this, usually only because of the lack of interfaces in these languages).

Also remember when using interfaces to adhere to the Interface Segregation Principle (ISP). ISP states that no client should be forced to depend on methods it does not use. For this reason interfaces should be focused on specific tasks and are usually very small (ex. IDisposable, IComparable ).

Another tip is if you are developing small, concise bits of functionality, use interfaces. If you are designing large functional units, use an abstract class.

Hope this clears things up for some people!

Also if you can think of any better examples or want to point something out, please do so in the comments below!




Another option to keep in mind is using the "has-a" relationship, aka "is implemented in terms of" or "composition." Sometimes this is a cleaner, more flexible way to structure things than using "is-a" inheritance.

It may not make as much sense logically to say that Dog and Cat both "have" a Pet, but it avoids common multiple inheritance pitfalls:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Yes, this example shows that there is a lot of code duplication and lack of elegance involved in doing things this way. But one should also appreciate that this helps to keep Dog and Cat decoupled from the Pet class (in that Dog and Cat do not have access to the private members of Pet), and it leaves room for Dog and Cat to inherit from something else--possibly the Mammal class.

Composition is preferable when no private access is required and you don't need to refer to Dog and Cat using generic Pet references/pointers. Interfaces give you that generic reference capability and can help cut down on the verbosity of your code, but they can also obfuscate things when they are poorly organized. Inheritance is useful when you need private member access, and in using it you are committing yourself to highly coupling your Dog and Cat classes to your Pet class, which is a steep cost to pay.

Between inheritance, composition, and interfaces there is no one way that is always right, and it helps to consider how all three options can be used in harmony. Of the three, inheritance is typically the option that should be used the least often.




Les interfaces devraient être petites. Vraiment petit. Si vous êtes en train de décomposer vos objets, vos interfaces ne contiendront probablement que quelques méthodes et propriétés très spécifiques.

Les classes abstraites sont des raccourcis. Y a-t-il des choses que tous les dérivés de PetBase partagent et que vous pouvez coder une seule fois? Si oui, alors il est temps pour une classe abstraite.

Les classes abstraites sont également limitatives. Bien qu'ils vous donnent un raccourci génial pour produire des objets enfants, n'importe quel objet donné peut seulement implémenter une classe abstraite. Plusieurs fois, je trouve cela une limitation des classes abstraites, et c'est pourquoi j'utilise beaucoup d'interfaces.

Les classes abstraites peuvent contenir plusieurs interfaces. Votre classe abstraite PetBase peut implémenter IPet (les familiers ont des propriétaires) et IDigestion (les animaux domestiques mangent, ou du moins ils devraient). Cependant, PetBase n'implémentera probablement pas IMammal, car tous les animaux domestiques ne sont pas des mammifères et tous les mammifères ne sont pas des animaux de compagnie. Vous pouvez ajouter un MammalPetBase qui étend PetBase et ajouter IMammal. FishBase pourrait avoir PetBase et ajouter IFish. IFish aurait ISwim et IUnderwaterBreather comme interfaces.

Oui, mon exemple est extrêmement compliqué pour l'exemple simple, mais cela fait partie de la grande chose à propos de la façon dont les interfaces et les classes abstraites fonctionnent ensemble.




Voici la définition basique et simple de l'interface et de la classe de base:

  • Classe de base = héritage d'objet.
  • Interface = héritage fonctionnel.

à votre santé




I've found that a pattern of Interface > Abstract > Concrete works in the following use-case:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

The abstract class defines default shared attributes of the concrete classes, yet enforces the interface. Par exemple:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Now, since all mammals have hair and nipples (AFAIK, I'm not a zoologist), we can roll this into the abstract base class

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

And then the concrete classes merely define that they walk upright.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

This design is nice when there are lots of concrete classes, and you don't want to maintain boilerplate just to program to an interface. If new methods were added to the interface, it would break all of the resulting classes, so you are still getting the advantages of the interface approach.

In this case, the abstract could just as well be concrete; however, the abstract designation helps to emphasize that this pattern is being employed.




Les commentaires précédents à propos de l'utilisation de classes abstraites pour une implémentation commune sont définitivement sur la bonne voie. Un avantage que je n'ai pas encore vu mentionné est que l'utilisation d'interfaces rend beaucoup plus facile la mise en œuvre d'objets fictifs aux fins de tests unitaires. Définir IPet et PetBase comme décrit par Jason Cohen vous permet de vous moquer facilement de différentes conditions de données, sans le surcoût d'une base de données physique (jusqu'à ce que vous décidiez qu'il est temps de tester la vraie chose).




Conceptually, an Interface is used to formally and semi-formally define a set of methods that an object will provide. Formally means a set of method names and signatures, semi-formally means human readable documentation associated with those methods. Interfaces are only descriptions of an API (after all, API stands for Application Programmer Interface ), they can't contain any implementation, and it's not possible to use or run an Interface. They only make explicit the contract of how you should interact with an object.

Classes provide an implementation, they can declare that they implement zero, one or more Interfaces. If a Class is intended to be inherited, the convention is to prefix the Class name with "Base".

There is a distinction between a Base Class and an Abstract Base Classes (ABC). ABCs mix interface and implementation together. Abstract outside of computer programming means "summary", that is "Abstract == Interface". An Abstract Base Class can then describe both an interface, as well as an empty, partial or complete implementation that is intended to be inherited.

Opinions on when to use Interfaces versus Abstract Base Classes versus just Classes is going to vary wildly based on both what you are developing, and which language you are developing in. Interfaces are often associated only with statically typed languages such as Java or C#, but dynamically typed languages can also have Interfaces and Abstract Base Classes. In Python for example, the distinction is made clear between a Class, which declares that it implements an Interface, and an object, which is an instance of a Class, and is said to provide that Interface. It's possible in a dynamic language that two objects that are both instances of the same Class, can declare that they provide completely different interfaces. In Python this is only possible for object attributes, while methods are shared state between all objects of a Class. However in Ruby, objects can have per-instance methods, so it's possible that the Interface between two objects of the same Class can vary as much as the programmer desires (however, Ruby doesn't have any explicit way of declaring Interfaces).

In dynamic languages the Interface to an object is often implicitly assumed, either by introspecting an object and asking it what methods it provides (Look Before You Leap) or preferably by simply attempting to use the desired Interface on an object and catching exceptions if the object doesn't provide that Interface (Easier to Ask Forgiveness than Permission). This can lead to "false positives" where two Interfaces have the same method name but are semantically different, however the trade-off is that your code is more flexible since you don't need to over specify up-front to anticipate all possible uses of your code.




En général, vous devriez privilégier les interfaces plutôt que les classes abstraites. Une raison d'utiliser une classe abstraite est si vous avez une implémentation commune parmi les classes concrètes. Bien sûr, vous devriez toujours déclarer une interface (IPet) et avoir une classe abstraite (PetBase) implémenter cette interface. En utilisant de petites interfaces distinctes, vous pouvez utiliser des multiples pour améliorer encore la flexibilité. Les interfaces permettent le maximum de flexibilité et de portabilité des types à travers les frontières. Lorsque vous passez des références à travers les frontières, passez toujours l'interface et non le type concret. Cela permet à l'extrémité réceptrice de déterminer la mise en œuvre concrète et offre une flexibilité maximale. Ceci est absolument vrai lors de la programmation dans un mode TDD / BDD.

Le Gang des Quatre a déclaré dans son livre "Parce que l'héritage expose une sous-classe aux détails de la mise en œuvre de ses parents, on dit souvent que" l'héritage rompt l'encapsulation ". Je crois que c'est vrai.




Expliqué bien dans cet article de Java World

Personnellement, j'ai tendance à utiliser des interfaces pour définir des interfaces - c'est-à-dire des parties de la conception du système qui spécifient comment quelque chose devrait être accessible.

Il n'est pas rare d'avoir une classe implémentant une ou plusieurs interfaces.

Cours abstraits que j'utilise comme base pour autre chose.

Ce qui suit est un extrait de l'article mentionné ci-dessus JavaWorld.com, auteur Tony Sintes, 20/04/01

Interface vs classe abstraite

Choisir des interfaces et des classes abstraites n'est pas une proposition soit / soit. Si vous devez changer votre conception, faites-en une interface. Cependant, vous pouvez avoir des classes abstraites qui fournissent un comportement par défaut. Les classes abstraites sont d'excellents candidats dans les cadres d'application.

Les classes abstraites vous permettent de définir certains comportements; ils forcent vos sous-classes à en fournir d'autres. Par exemple, si vous disposez d'une infrastructure d'application, une classe abstraite peut fournir des services par défaut, tels que la gestion des événements et des messages. Ces services permettent à votre application de se connecter à votre infrastructure d'application. Cependant, il existe certaines fonctionnalités spécifiques à l'application que seule votre application peut effectuer. Une telle fonctionnalité peut inclure des tâches de démarrage et d'arrêt, qui dépendent souvent de l'application. Ainsi, au lieu d'essayer de définir ce comportement lui-même, la classe de base abstraite peut déclarer des méthodes de fermeture et de démarrage abstraites. La classe de base sait qu'elle a besoin de ces méthodes, mais une classe abstraite permet à votre classe d'admettre qu'elle ne sait pas comment effectuer ces actions; il sait seulement qu'il doit initier les actions. Quand il est temps de démarrer, la classe abstraite peut appeler la méthode de démarrage. Lorsque la classe de base appelle cette méthode, Java appelle la méthode définie par la classe enfant.

De nombreux développeurs oublient qu'une classe qui définit une méthode abstraite peut aussi appeler cette méthode. Les classes abstraites constituent un excellent moyen de créer des hiérarchies d'héritage planifiées. Ils sont également un bon choix pour les classes non-feuilles dans les hiérarchies de classes.

Classe vs interface

Certains disent que vous devriez définir toutes les classes en termes d'interfaces, mais je pense que la recommandation semble un peu extrême. J'utilise des interfaces quand je vois que quelque chose dans ma conception va changer fréquemment.

Par exemple, le modèle Stratégie vous permet d'échanger de nouveaux algorithmes et processus dans votre programme sans modifier les objets qui les utilisent. Un lecteur multimédia peut savoir comment lire des CD, des MP3 et des fichiers WAV. Bien sûr, vous ne voulez pas coder en dur ces algorithmes de lecture dans le lecteur; cela rendra difficile l'ajout d'un nouveau format comme AVI. En outre, votre code sera jonché de déclarations de cas inutiles. Et pour ajouter l'insulte à la blessure, vous devrez mettre à jour ces instructions chaque fois que vous ajoutez un nouvel algorithme. Dans l'ensemble, ce n'est pas une manière très orientée objet de programmer.

Avec le pattern Strategy, vous pouvez simplement encapsuler l'algorithme derrière un objet. Si vous faites cela, vous pouvez fournir des plug-ins de nouveaux médias à tout moment. Appelons la classe de plug-in MediaStrategy. Cet objet aurait une méthode: playStream (Stream s). Donc, pour ajouter un nouvel algorithme, nous étendons simplement notre classe d'algorithme. Maintenant, lorsque le programme rencontre le nouveau type de média, il délègue simplement la diffusion du flux à notre stratégie média. Bien sûr, vous aurez besoin de plomberie pour instancier correctement les stratégies d'algorithme dont vous aurez besoin.

C'est un excellent endroit pour utiliser une interface. Nous avons utilisé le pattern Strategy, qui indique clairement une place dans le design qui va changer. Ainsi, vous devez définir la stratégie en tant qu'interface. Vous devriez généralement favoriser les interfaces sur l'héritage quand vous voulez qu'un objet ait un certain type; dans ce cas, MediaStrategy. S'appuyer sur l'héritage pour l'identité de type est dangereux; il vous verrouille dans une hiérarchie d'héritage particulière. Java n'autorise pas l'héritage multiple, donc vous ne pouvez pas étendre quelque chose qui vous donne une implémentation utile ou plus d'identité de type.




Regarding C#, in some senses interfaces and abstract classes can be interchangeable. However, the differences are: i) interfaces cannot implement code; ii) because of this, interfaces cannot call further up the stack to subclass; and iii) only can abstract class may be inherited on a class, whereas multiple interfaces may be implemented on a class.




Prefer interfaces over abstract classes

Rationale, the main points to consider [two already mentioned here] are :

  • Interfaces are more flexible, because a class can implement multiple interfaces. Since Java does not have multiple inheritance, using abstract classes prevents your users from using any other class hierarchy. In general, prefer interfaces when there are no default implementations or state. Java collections offer good examples of this (Map, Set, etc.).
  • Abstract classes have the advantage of allowing better forward compatibility. Once clients use an interface, you cannot change it; if they use an abstract class, you can still add behavior without breaking existing code. If compatibility is a concern, consider using abstract classes.
  • Even if you do have default implementations or internal state, consider offering an interface and an abstract implementation of it . This will assist clients, but still allow them greater freedom if desired [1].
    Of course, the subject has been discussed at length elsewhere [2,3].

[1] It adds more code, of course, but if brevity is your primary concern, you probably should have avoided Java in the first place!

[2] Joshua Bloch, Effective Java, items 16-18.

[3] http://www.codeproject.com/KB/ar ...




@Joel: Some languages (eg, C++) allow multiple-inheritance.




J'ai une règle grossière

Fonctionnalité: susceptible d'être différente dans toutes les parties: Interface.

Les données, et la fonctionnalité, les parties seront pour la plupart les mêmes, les parties différentes: la classe abstraite.

Les données et la fonctionnalité fonctionnent réellement, si elles ne sont étendues qu'avec de légères modifications: classe ordinaire (concrète)

Données et fonctionnalités, aucun changement prévu: classe ordinaire (concrète) avec modificateur final.

Données, et peut-être fonctionnalité: lecture seule: membres de l'énumération.

C'est très approximatif et prêt et pas du tout strictement défini, mais il y a un spectre d'interfaces où tout est destiné à être changé en enums où tout est fixé un peu comme un fichier en lecture seule.




Eh bien, Josh Bloch s'est dit dans Effective Java 2d :

Préférer les interfaces sur les classes abstraites

Quelques points principaux:

  • Les classes existantes peuvent être facilement adaptées pour implémenter une nouvelle interface . Tout ce que vous avez à faire est d'ajouter les méthodes requises si elles n'existent pas encore et ajoutez une clause implements à la déclaration de classe.

  • Les interfaces sont idéales pour définir les mixins . Autrement dit, un mixin est un type qu'une classe peut implémenter en plus de son "type primaire" pour déclarer qu'il fournit un comportement optionnel. Par exemple, Comparable est une interface mixin qui permet à une classe de déclarer que ses instances sont ordonnées par rapport à d'autres objets mutuellement comparables.

  • Les interfaces permettent la construction de frameworks de type non hiérarchique . Les hiérarchies de types sont idéales pour organiser certaines choses, mais d'autres ne tombent pas dans une hiérarchie rigide.

  • Les interfaces permettent des améliorations de fonctionnalités sûres et puissantes via l'idiome de classe wrap-per. Si vous utilisez des classes abstraites pour définir des types, vous quittez le programmeur qui veut ajouter une fonctionnalité sans autre alternative que d'utiliser l'héritage.

De plus, vous pouvez combiner les vertus des interfaces et des classes abstraites en fournissant une classe d'implémentation squelette abstraite pour chaque interface non triviale que vous exportez.

D'un autre côté, les interfaces sont très difficiles à faire évoluer. Si vous ajoutez une méthode à une interface, toutes ses implémentations seront interrompues.

PS .: Achetez le livre. C'est beaucoup plus détaillé.