54 votes

Interfaces: Pourquoi je ne peux pas sembler à les saisir?

Quelqu'un pourrait-il svp démystifier les interfaces pour moi ou m'indiquer quelques bons exemples. Je continue à voir des interfaces popup ici et là, mais je n'ai jamais vraiment été exposés à de bonnes explications des interfaces ou quand les utiliser?

Je parle d'interfaces dans le contexte des interfaces vs les classes abstraites

78voto

Rob Conery Points 10930

Interfaces vous permet de programmer l'encontre d'une "description" au lieu d'un type, qui permet à plus vaguement associer des éléments de votre logiciel.

Pensez-y de cette façon: Vous souhaitez partager des données avec quelqu'un dans le cube à côté de vous, si vous retirez votre clé usb et copier/coller. Vous marchez à côté de la porte et le gars dit "c'est que l'USB?" et vous dites oui - tous ensemble. Il n'a pas d'importance la taille de la mémoire flash bâton, ni le maker - tout ce qui importe, c'est que c'est de l'USB.

De la même manière, les interfaces vous permettent de generisize votre développement. En utilisant une autre analogie, imaginez que vous vouliez créer une application qui pratiquement peint les voitures. Vous pourriez avoir une signature comme ceci:

public void Paint(Car car, System.Drawing.Color color)...

Ce serait le travail jusqu'à ce que votre client a dit "maintenant je veux peindre les camions", de sorte que vous pourriez faire ceci:

public void Paint (Vehicle vehicle, System.Drawing.Color color)...

cela permettrait d'élargir votre app... jusqu'à ce que votre client a dit "maintenant, je veux peindre des maisons!" Ce que vous avez pu faire depuis le début, il est créé une interface:

public interface IPaintable{
   void Paint(System.Drawing.Color color);
}

...et transmis à votre routine:

public void Paint(IPaintable item, System.Drawing.Color color){
   item.Paint(color);
}

Espérons que cela a du sens - c'est une jolie explication simpliste mais j'espère que devient le cœur de celui-ci.

45voto

JoshReedSchramm Points 1729

Les Interfaces d'établir un contrat entre une classe et le code qui l'appelle. Ils vous permettent également avoir les mêmes classes qui implémentent la même interface, mais ne les différentes actions ou d'événements et de ne pas savoir qui vous êtes vraiment travailler avec. Cela pourrait faire plus de sens, comme un exemple, laissez-moi donc essayer ici.

Disons que vous avez un couple de classes de Chien, de Chat et de la Souris. Chacune de ces classes est un Animal, et en théorie, vous pourriez hériter à partir d'une autre classe appelée Animal de compagnie, mais voici le problème. Les animaux de compagnie et de ne pas faire n'importe quoi. Vous ne pouvez pas aller au magasin et acheter un animal de compagnie. Vous pouvez aller acheter un chien ou un chat, mais un animal de compagnie est une notion abstraite et non pas de la réalité.

Si Vous connaissez les animaux de compagnie peuvent faire certaines choses. Ils peuvent dormir, ou manger, etc. Si vous définissez une interface appelée IPet et il ressemble à quelque chose comme ceci (la syntaxe C#)

public interface IPet
{
    void Eat(object food);
    void Sleep(int duration);
}

Chacun de votre Chien, votre Chat et de la Souris classes implémentent IPet.

public class Dog : IPet

Alors maintenant, chacune de ces classes est d'avoir sa propre implémentation de Manger et de Dormir. Yay vous avez un contrat... Maintenant, quel est le point.

Prochaine disons que vous voulez faire un nouvel objet appelé PetStore. Et ce n'est pas une très bonne PetStore de sorte qu'ils fondamentalement juste vous vendre un hasard animal de compagnie (oui je sais c'est un exemple artificiel).

public class PetStore
{
     public static IPet GetRandomPet()
     {    
          //Code to return a random Dog, Cat, or Mouse
     } 
}

IPet myNewRandomPet = PetStore.GetRandomPet();
myNewRandomPet.Sleep(10);

Le problème est que vous ne savez pas quel type d'animal de compagnie, il sera. Grâce à l'interface si vous savez ce que c'est qu'elle va Manger et Dormir.

Si cette réponse peut ne pas avoir été utile à tous, mais l'idée générale est que les interfaces vous permettent de faire bien des choses, comme l'Injection de Dépendance et d'Inversion de Contrôle où vous pouvez obtenir un objet, avoir une liste de choses que l'objet peut faire sans jamais VRAIMENT savoir ce que le type concret de cet objet.

33voto

Bob King Points 12913

La réponse la plus facile est que les interfaces de définir ce que votre classe peut le faire. C'est un "contrat" qui dit que votre classe sera en mesure de faire cette action.

Public Interface IRollOver
    Sub RollOver()
End Interface

Public Class Dog Implements IRollOver
    Public Sub RollOver() Implements IRollOver.RollOver
        Console.WriteLine("Rolling Over!")
    End Sub
End Class

Public Sub Main()
    Dim d as New Dog()
    Dim ro as IRollOver = TryCast(d, IRollOver)
    If ro isNot Nothing Then
        ro.RollOver()
    End If
End Sub

Fondamentalement, vous garantissant que la classe Chien a toujours la possibilité de rouler aussi longtemps qu'il continue à mettre en œuvre que de l'Interface. Les chats devraient jamais obtenir la possibilité de Survol(), ils pourraient mettre en œuvre cette interface, et vous pouvez traiter les Chiens et les Chats de façon homogène lorsque leur demandant d'Annuler().

16voto

Alexander Points 4298

Lorsque vous conduisez la voiture d'un ami, vous avez plus ou moins de savoir comment le faire. C'est parce que les voitures classiques ont tous une interface très similaire: le volant, les pédales, et ainsi de suite. Pensez à cette interface comme un contrat entre les constructeurs automobiles et les conducteurs. En tant que conducteur (l'utilisateur/client de l'interface du logiciel dans les conditions), vous n'avez pas besoin d'apprendre les détails de voitures différentes pour être en mesure de conduire: par exemple, tout ce que vous devez savoir, c'est qu'à tourner le volant rend la voiture à son tour. En tant que fabricant de voitures (le fournisseur de l'implémentation de l'interface du logiciel dans les conditions), vous avez une idée claire de ce que votre nouvelle voiture devrait avoir et comment il doit se comporter de manière que les conducteurs peuvent utiliser sans beaucoup de formation supplémentaire. Ce contrat est que les gens, dans la conception de logiciels de se référer à découplage (l'utilisateur à partir du fournisseur) -- le code du client est en termes d'utilisation d'une interface plutôt qu'une mise en oeuvre particulière de celle-ci et, partant, n'a pas besoin de connaître les détails des objets de mise en œuvre de l'interface.

14voto

Robert Paulson Points 10792

Les Interfaces sont un mécanisme pour réduire le couplage entre autre, peut-être des parties disparates d'un système.

À partir d'un .net point de vue

  • la définition de l'interface, une liste d'activités et/ou des propriétés
  • les méthodes d'interface sont toujours publics
  • l'interface elle-même n'a pas à être public

Lorsque vous créez une classe qui implémente l'interface, vous devez fournir soit explicite ou implicite de la mise en œuvre de toutes les méthodes et propriétés définies par l'interface.

Plus loin .net a seulement de l'héritage simple, et les interfaces sont une nécessité pour un objet à exposer des méthodes à d'autres objets qui ne sont pas au courant, ou se trouvent en dehors de la hiérarchie de classe. Parfois, nous appelons cela de l'exposer à des comportements.

Un exemple un peu plus concret:

Considérer est que nous avons beaucoup de DTO (les objets de transfert de données) qui ont des propriétés pour qui la dernière mise à jour, et quand c'était. Le problème est que pas toutes les DTO ont cette propriété, car il n'est pas toujours pertinente. Dans le même temps, nous désirons un mécanisme générique pour garantir ces propriétés sont définies si disponible lorsqu'il est soumis à des flux de travail, mais le flux de travail de l'objet doit être faiblement couplé à partir des objets. c'est à dire la soumission de la méthode de workflow ne devrait pas vraiment au courant de toutes les subtilités de chaque objet et tous les objets dans le flux de travail ne sont pas nécessairement DTO objets.

// first pass - not maintainable
void SubmitToWorkflow(object o, User u)
{
  if( o is StreetMap )
  {
     var map = (StreetMap)o;
     map.LastUpdated = DateTime.UtcNow;
     map.UpdatedByUser = u.UserID;
  }
  else if( o is Person )
  {
     var person = (Person)o;
     person.LastUpdated = DateTime.Now; // whoops .. should be UtcNow
     person.UpdatedByUser = u.UserID;
  }
  // whoa - very unmaintainable.

Dans le code ci-dessus, SubmitToWorkflow() devez savoir à propos de chaque objet. En outre, le code est un gâchis avec un énorme if/else/switch, viole l'Don't Repeat Yourself (SEC) principe, et oblige les développeurs à se souvenir de copier/coller des changements à chaque fois qu'un nouvel objet est ajouté au système.

// second pass - brittle
void SubmitToWorkflow(object o, User u)
{
  if( o is DTOBase )
  {
     DTOBase dto = (DTOBase)o;
     dto.LastUpdated = DateTime.UtcNow;
     dto.UpdatedByUser = u.UserID;
  }

Un peu mieux, mais encore fragile. Si nous voulons nous soumettre d'autres types d'objets, nous avons besoin d'encore besoin de plus de cas annuels. etc.

// third pass pass - also brittle
void SubmitToWorkflow(DTOBase dto, User u)
{
  dto.LastUpdated = DateTime.UtcNow;
  dto.UpdatedByUser = u.UserID;

Encore fragile, et les deux méthodes d'imposer la contrainte que tous les DTO ont à mettre en œuvre cette propriété, ce qui nous a indiqué n'était pas universellement applicable. Certains développeurs pourraient être tentés d'écrire ne rien faire, mais qui sent mauvais. Nous ne voulons pas que les classes qui prétendent la mise à jour du suivi, mais ne le font pas.

Interfaces, comment peuvent-ils aider?

Si l'on définit une interface très simple:

public interface IUpdateTracked
{
  DateTime LastUpdated { get; set; }
  int UpdatedByUser { get; set; }
}

Toute la classe qui a besoin de cette mise à jour automatique de suivi peut implémenter l'interface.

public class SomeDTO : IUpdateTracked
{
  // IUpdateTracked implementation as well as other methods for SomeDTO
}

La méthode de workflow peut être fait pour être beaucoup plus générique, plus petite, et plus facile à gérer, et continuera à travailler n'importe comment beaucoup de classes implémentent l'interface (DTO ou autre) car il ne traite que de l'interface.

void SubmitToWorkflow(object o, User u)
{
  IUpdateTracked updateTracked = o as IUpdateTracked;
  if( updateTracked != null )
  {
     updateTracked.LastUpdated = DateTime.UtcNow;
     updateTracked.UpdatedByUser = u.UserID;
  }
  // ...
  • On peut noter la variation void SubmitToWorkflow(IUpdateTracked updateTracked, User u) permettrait de garantir la sécurité de type, toutefois, il ne semble pas appropriée dans ces circonstances.

Dans certains code de production que nous utilisons, nous avons de génération de code pour crée ces DTO classes à partir de la base de données de définition. La seule chose que le développeur n'est d'avoir créer le champ nom correctement et décorer la classe avec l'interface. Tant que les propriétés sont appelés LastUpdated et UpdatedByUser, il fonctionne, tout simplement.

Peut-être que vous vous demandez Ce qui se passe si ma base de données est l'héritage et ce n'est pas possible? Vous avez juste à faire un peu plus de la frappe; une autre grande caractéristique des interfaces, ils peuvent vous permettre de créer un pont entre les classes.

Dans le code ci-dessous, nous avons une fictifs LegacyDTO, un objet préexistant ayant même nom de champs. C'est la mise en œuvre de la IUpdateTracked interface de pont de l'existant, mais différemment des propriétés nommées.

// using an interface to bridge properties
public class LegacyDTO : IUpdateTracked
{ 
    public int LegacyUserID { get; set; }
    public DateTime LastSaved { get; set; }

    public int UpdatedByUser
    {
        get { return LegacyUserID; }
        set { LegacyUserID = value; }
    }
    public DateTime LastUpdated
    {
        get { return LastSaved; }
        set { LastSaved = value; }
    }
}  

Vous pourriez chose de Cool, mais n'est-il pas confusion ayant de multiples propriétés? ou Ce qui se passe si il y a déjà ces propriétés, mais elles veulent dire quelque chose d'autre? .net vous donne explicitement la capacité de mettre en œuvre l'interface. Ce que cela signifie, c'est que le IUpdateTracked propriétés ne sera visible que lorsque nous sommes à l'aide d'une référence à IUpdateTracked. Notez comment il n'y a pas de modificateur sur la déclaration et la déclaration comprend le nom de l'interface.

// explicit implementation of an interface
public class YetAnotherObject : IUpdatable
{ 
    int IUpdatable.UpdatedByUser
    { ... }
    DateTime IUpdatable.LastUpdated
    { ... }

Le fait d'avoir autant de souplesse pour définir la façon dont la classe implémente l'interface permet au développeur de beaucoup de liberté afin de dissocier l'objet de méthodes qui la consomment. Les Interfaces sont un excellent moyen de briser le couplage.

Il y a beaucoup plus à des interfaces de tout cela. C'est juste une simplification de la vie réelle exemple qui utilise un seul aspect de l'interface en fonction de la programmation.

Comme je l'ai mentionné plus tôt, et par d'autres intervenants, vous pouvez créer des méthodes qui prennent et/ou le retour à l'interface des références plutôt que sur une classe de référence. Si je devais trouver les doublons dans une liste, je pourrais écrire une méthode qui prend en entrée et retourne un IList (une interface qui définit les opérations que le travail sur les listes) et je ne suis pas limité à un béton de classe de collection.

// decouples the caller and the code as both 
// operate only on IList, and are free to swap
// out the concrete collection.
public IList<T> FindDuplicates( IList<T> list )
{
    var duplicates = new List<T>()
    // TODO - write some code to detect duplicate items
    return duplicates;
}

Versioning mise en garde

Si c'est une interface publique, vous êtes déclarant j'ai la garantie de l'interface x ressemble à ça! et une fois que vous avez envoyé le code et publié l'interface, vous ne devriez jamais changer. Dès que le code de la consommation commence à s'appuyer sur cette interface, vous ne voulez pas casser leur code dans le champ.

Voir ce Haacked post pour une bonne discussion.

Interfaces rapport résumé (de base) des classes

Les classes abstraites peuvent fournir de la mise en œuvre tandis que les Interfaces ne peuvent pas. Les classes abstraites sont à certains égards plus de souplesse dans la gestion des versions aspect si vous suivez quelques règles comme le NVPI (Non Virtuelle Interface Publique) modèle.

Il est bon de répéter que, dans .net, une classe ne peut hériter que d'une seule classe, mais une classe peut implémenter autant d'interfaces qu'elle aime.

L'Injection De Dépendance

Le résumé rapide des interfaces et DI est que l'utilisation des interfaces permet aux développeurs d'écrire du code qui est programmé à l'encontre d'une interface pour fournir des services. Dans la pratique, vous pouvez vous retrouver avec beaucoup de petites interfaces et des classes de petite taille, et une idée est que les classes de petite taille qui font une chose et une seule chose est beaucoup plus facile à coder et à maintenir.

class AnnualRaiseAdjuster
   : ISalaryAdjuster
{
   AnnualRaiseAdjuster(IPayGradeDetermination payGradeDetermination) { ...  }

   void AdjustSalary(Staff s)
   {
      var payGrade = payGradeDetermination.Determine(s);
      s.Salary = s.Salary * 1.01 + payGrade.Bonus;
   }
}

En bref, l'intérêt montré dans l'extrait ci-dessus est que la catégorie de salaire détermination est juste injecté dans l'examen annuel de soulever de réglage. Comment payer le grade est déterminé n'est pas vraiment de l'importance à cette classe. Lors de l'essai, le développeur peut se moquer de l'échelle salariale de la détermination des résultats afin d'assurer le salaire de réglage des fonctions comme souhaité. Les tests sont aussi rapides, parce que le test est seulement de tester la classe, et pas tout le reste.

Ce n'est pas un DI apprêt même si, comme il y a des livres entiers consacrés à ce sujet; l'exemple ci-dessus est très simplifiée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X