Frage

Könnte jemand bitte Schnittstellen für mich entmystifizieren oder mir Beispiele zu einigen guten Punkt? Ich halte Schnittstellen Pop-up hier und da zu sehen, aber ich habe noch nie wirklich gute Erklärungen von Schnittstellen ausgesetzt war oder wenn sie zu benutzen.

Ich spreche über Schnittstellen in einem Kontext von Schnittstellen vs. abstrakten Klassen.

War es hilfreich?

Lösung

Schnittstellen können Sie gegen eine „Beschreibung“ programmieren, anstatt eine Art, die Sie mehr-lose assoziierten Elemente Ihrer Software ermöglicht.

Denken auf diese Weise davon: Sie wollen neben dir im Würfel mit jemandem Daten gemeinsam nutzen, so dass Sie Ihren Flash-Stick und Copy / Paste herausziehen. Sie gehen nebenan und der Typ sagt „ist, dass USB?“ und Sie sagen ja - ganz eingestellt. Es ist nicht die Größe des Flash-Stick Rolle, noch der Hersteller -. Alles, was zählt ist, dass es USB

In der gleichen Weise, Schnittstellen können Sie Ihre Entwicklung generisize. Unter Verwendung eines anderen Analogie - stellen Sie sich eine Anwendung, die so gut wie gemalt Autos schaffen wollte. Vielleicht haben Sie eine Signatur wie folgt aussehen:

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

Das würde funktionieren, bis Ihr Kunde sagte: „ich jetzt Lastwagen malen wollen“, so können Sie dies tun:

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

Dies würde Ihre Anwendung erweitern ... bis Ihr Kunde sagte: „Ich möchte jetzt Häuser malen!“ Was Sie von Anfang an hätte tun können, ist eine Schnittstelle erstellt:

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

... und übergeben, das zu Ihrer Routine:

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

Hoffentlich macht Sinn - es ist eine ziemlich vereinfachte Erklärung ist aber hoffentlich trifft den Kern davon

.

Andere Tipps

Schnittstellen schafft einen Vertrag zwischen einer Klasse und dem Code, der es nennt. Sie ermöglichen es, auch ähnliche Klassen zu haben, die die gleiche Schnittstelle implementieren, sondern tun verschiedene Aktionen oder Ereignisse und müssen nicht wissen, welche Sie tatsächlich arbeiten. Dies könnte mehr Sinn machen als ein Beispiel mir eine hier so lassen versuchen.

Angenommen, Sie haben ein paar Klassen genannt Hund, Katze und Maus. Jede dieser Klassen ist ein Haustier und in der Theorie könnte man sie alle von einer anderen Klasse erbt namens Pet aber hier ist das Problem. Tiere und von sich selbst tun nichts. Sie können nicht in den Laden gehen und ein Haustier kaufen. Sie können einen Hund oder eine Katze gehen und kaufen, aber ein Tier ist ein abstraktes Konzept und nicht konkret.

So wissen Sie Haustiere, bestimmte Dinge tun. Sie können schlafen oder essen, etc. So können Sie eine Schnittstelle namens IPET definieren und es sieht so etwas wie diese (C # Syntax)

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

Jeder Ihrer Hund, Katze und Maus-Klassen implementieren IPET.

public class Dog : IPet

So, jetzt jede dieser Klassen hat, um es eine eigene Implementierung von Essen und Schlafen ist. Yay Sie haben einen Vertrag ... Nun, was ist der Punkt.

Als nächstes lassen Sie uns sagen Sie ein neues Objekt machen wollen genannt PetStore. Und das ist kein sehr guter PetStore, so dass sie im Grunde einfach ein zufälliges Tier verkaufen (ja, ich weiß, das ist ein konstruiertes Beispiel).

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

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

Das Problem ist, dass Sie nicht wissen, welche Art von Tier es sein wird. obwohl Dank der Schnittstelle wissen Sie, was auch immer es ist, wird es essen und schlafen.

So diese Antwort nicht hilfreich gewesen, überhaupt aber die allgemeine Idee ist, dass Schnittstellen können Sie nette Dinge wie Dependency Injection und Inversion of Control tun, wo Sie ein Objekt bekommen, hat eine gut definierte Liste von Sachen, die tun Objekt kann ohne jemals wirklich zu wissen, was die konkrete Art des Objekts ist.

Die einfachste Antwort ist, dass Schnittstellen definiert ein, was Ihre Klasse tun. Es ist ein „Vertrag“, das besagt, dass Ihre Klasse in der Lage sein wird, diese Aktion zu tun.

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

Grundsätzlich sind Sie garantiert, dass die Dog-Klasse immer die Möglichkeit, so lange zu rollen hat, wie es die Schnittstelle zu implementieren, wird fortgesetzt. Sollte Katzen immer die Möglichkeit, Rollover () gewinnen auch sie diese Schnittstelle implementieren könnte, und Sie können beide Hunde und Katzen homogen behandeln, wenn sie zu fragen, Rollover ().

Wenn Sie einen Freundes Auto fahren, können Sie mehr oder weniger wissen, wie das zu tun. Dies liegt daran, herkömmliche Autos, die alle eine sehr ähnliche Schnittstelle haben: Lenkrad, Pedale, und so weiter. Denken Sie an diese Schnittstelle als ein Vertrag zwischen Automobilhersteller und Fahrer. Als Fahrer (Anwender / Client der Schnittstelle in der Software-Bedingungen), die Sie nicht die Angaben der verschiedenen Fahrzeuge lernen müssen in der Lage zu sein, sie zu fahren: zum Beispiel alles, was Sie wissen müssen, ist, dass das Lenkrad dreht das macht Auto drehen. Als Automobilhersteller (der Anbieter von einer Implementierung der Schnittstelle in der Software-Begriffe) haben Sie eine klare Vorstellung davon, was Ihr neues Auto haben sollte und wie sie sich verhalten, so dass die Fahrer sie ohne viel zusätzliche Ausbildung nutzen können. Dieser Vertrag ist, was die Menschen in Software-Design als Entkopplung beziehen (der Benutzer vom Provider) - der Client-Code in Bezug auf eine Schnittstelle, anstatt eine bestimmte Implementierung davon verwenden und daher muss nicht die Details der Objekte wissen Implementierung der Schnittstelle.

Interfaces sind ein Mechanismus Kopplung zwischen verschiedenen, möglicherweise unterschiedlichen Teilen eines Systems zu reduzieren.

Aus .NET Perspektive

  • Die Schnittstellendefinition ist eine Liste von Operationen und / oder Eigenschaften.
  • Interface-Methoden sind immer öffentlich.
  • Die Schnittstelle selbst muss nicht öffentlich.

Wenn Sie eine Klasse erstellen, die Geräte die Schnittstelle, müssen Sie entweder eine explizite oder implizite Umsetzung aller Methoden und Eigenschaften durch die Schnittstelle definiert.

Ferner hat .NET nur einfache Vererbung und Schnittstellen sind eine Notwendigkeit für ein Objekt Methoden auf andere Objekte zu machen, die nicht bewusst sind, oder liegen außerhalb seiner Klassenhierarchie. Dies wird auch als Belichtungsverhalten bekannt.

Ein Beispiel, das ein wenig konkreter ist:

Betrachten

ist, dass wir viele DTO (Datentransferobjekte) haben, die Eigenschaften aufweisen, für die letzte Aktualisierung, und wann das war. Das Problem ist, dass nicht alle DTO diese Eigenschaft haben, weil es nicht immer relevant ist.

Zur gleichen Zeit wollen wir einen allgemeinen Mechanismus dieser Eigenschaften zu gewährleisten, wird gesetzt, wenn verfügbar, wenn an den Workflow eingereicht, aber das Workflow-Objekt soll lose aus den eingereichten Objekten gekoppelt werden. das heißt, die Workflow-Verfahren einreichen sollte nicht wirklich wissen, über all die Feinheiten der einzelnen Objekte und alle Objekte im Workflow sind nicht unbedingt DTO-Objekte.

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

In dem obigen Code, SubmitToWorkflow() muss über jedes Objekt kennen. Darüber hinaus ist der Code für ein Durcheinander mit einem massiven if / else / Schalter, verstößt gegen das nicht wiederholen selbst (DRY) Prinzip und erfordert Entwickler kopieren / Einfügen erinnern, jedes Mal ein neues Objekt ändert zu dem System hinzugefügt wird.

// 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;
  }

Es ist etwas besser, aber es ist immer noch brüchig. Wenn wir andere Arten von Objekten einreichen wollen, müssen wir noch mehr case-Anweisungen benötigen. etc.

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

Es ist noch brüchig, und beide Methoden die Beschränkung auferlegen, dass alle DTOs diese Eigenschaft implementieren, die wir angegeben nicht universell einsetzbar war. Einige Entwickler könnten versucht sein, zu schreiben, do-nothing Methoden, aber das riecht schlecht. Wir wollen keine Klassen vorgeben, sie Update-Tracking unterstützen aber nicht.

Schnittstellen, wie sie helfen können?

Wenn wir definieren eine sehr einfache Schnittstelle:

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

Jede Klasse, die dieses automatische Update-Tracking benötigt, kann die Schnittstelle implementieren.

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

Die Workflow-Methode kann viel allgemeinere, kleiner und besser verwaltbar sein gemacht werden, und es wird auch weiterhin unabhängig arbeiten, wie viele Klassen implementieren die Schnittstelle (DTOs oder auf andere Weise), weil es nur mit der Schnittstelle beschäftigt.

void SubmitToWorkflow(object o, User u)
{
  IUpdateTracked updateTracked = o as IUpdateTracked;
  if (updateTracked != null)
  {
     updateTracked.LastUpdated = DateTime.UtcNow;
     updateTracked.UpdatedByUser = u.UserID;
  }
  // ...
  • Wir können die Variation void SubmitToWorkflow(IUpdateTracked updateTracked, User u) beachten würde Art Sicherheit garantieren, aber es nicht als relevant unter diesen Umständen erscheinen.

In einigen Produktionscode verwenden wir, haben wir Code-Generierung diese DTO Klassen aus der Datenbankdefinition zu erstellen. Das einzige, was der Entwickler tut, ist haben die Feldnamen korrekt erstellen und die Klasse mit dem Interface dekorieren. Solange die Eigenschaften und Lastupdated UpdatedByUser genannt, es funktioniert einfach.

Vielleicht sind Sie gefragt Was passiert, wenn meine Datenbank Erbe ist, und das ist nicht möglich Sie nur ein wenig mehr Typisierung zu tun?; Eine andere große Eigenschaft von Schnittstellen ist sie ermöglichen es Ihnen, eine Brücke zwischen den Klassen zu erstellen.

In dem folgenden Code haben wir eine fiktive LegacyDTO, ein bereits bestehendes Objekt ähnlich benannten Felder mit. Es ist die IUpdateTracked Schnittstelle implementiert, die bestehenden, zu überbrücken, aber anders Eigenschaften genannt.

// 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; }
    }
}

Das könnte dir was Cool, aber isn‘t es verwirrend mehrere Eigenschaften? oder Was passiert, wenn es passiert, sind bereits diese Eigenschaften, aber sie bedeuten etwas anderes? .NET gibt Ihnen die Möglichkeit, die Schnittstelle explizit zu implementieren.

Was dies bedeutet, ist, dass die IUpdateTracked Eigenschaften nur sichtbar sein werden, wenn wir einen Verweis auf IUpdateTracked verwenden. Beachten Sie, wie es keine öffentlichen Modifikator auf die Erklärung und die Erklärung enthält den Namen der Schnittstelle.

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

Mit so viel Flexibilität zu definieren, wie die Klasse die Schnittstelle implementiert, gibt dem Entwickler eine Menge Freiheit des Objekts von Methoden zu entkoppeln, die sie verbrauchen. Schnittstellen ist eine gute Möglichkeit, Kopplung zu brechen.

Es gibt viel mehr Schnittstellen als nur dies. Dies ist nur ein vereinfachtes Beispiel aus der Praxis, die einen Aspekt der Schnittstelle basierter Programmierung verwendet.

Wie ich bereits erwähnt, und von anderen Responder können Sie Methoden erstellen, die nehmen und / oder Interface-Referenzen zurückgeben, statt einer bestimmten Klasse Referenz. Wenn ich brauche Duplikate in einer Liste zu finden, konnte ich eine Methode schreiben, nimmt und gibt eine IList (ein Schnittstelle Operationen, dass die Arbeit auf Listen definieren) und ich bin nicht auf eine konkrete Sammlung Klasse beschränkt.

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

Wenn es sich um eine öffentliche Schnittstelle ist, die Sie deklarieren Ich garantiere Schnittstelle x wie folgt aussieht! Und wenn Sie Code versendet haben und veröffentlicht die Schnittstelle, sollten Sie es sich nie ändern. Sobald der Verbraucher Code beginnt an dieser Schnittstelle zu verlassen, Sie nicht wollen, ihren Code auf dem Gebiet zu brechen.

Siehe diese Haacked für eine gute Diskussion veröffentlichen .

Schnittstellen im Vergleich zu abstrakt (Basis) Klassen

Abstrakte Klassen können Implementierung zur Verfügung stellen, während Schnittstellen nicht. Abstrakte Klassen sind in gewisser Weise flexibler in der Versionierung Aspekt, wenn Sie einige Richtlinien, wie die NVPI (Non-Virtuelle öffentliche Schnittstelle) Muster folgen.

Es lohnt sich wiederholen, dass in .NET kann eine Klasse nur von einer einzigen Klasse erben, sondern eine Klasse kann beliebig viele Schnittstellen implementieren, wie es mag.

Dependency Injection

Die kurze Zusammenfassung der Schnittstellen und Dependency Injection (DI) ist, dass die Verwendung von Schnittstellen-Entwickler Code schreiben kann, die gegen eine Schnittstelle programmiert ist, Dienstleistungen zu erbringen. In der Praxis kann man mit vielen kleinen Schnittstellen und kleinen Klassen am Ende, und eine Idee ist, dass kleine Klassen, die eine Sache und nur eine Sache tun, sind viel einfacher zu codieren und zu erhalten.

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

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

Kurz gesagt, ist der Nutzen in der obigen Schnipsel gezeigt ist, dass die Gehaltsbestimmung wird nur in die jährliche Erhöhung Teller injiziert. Wie Gehaltsstufe bestimmt wird, zu dieser Klasse nicht wirklich wichtig ist. Bei der Prüfung kann der Entwickler Gehaltsbestimmungsergebnisse verspotten die Gehaltsteller-Funktionen, um sicherzustellen, wie gewünscht. Die Tests sind auch schnell, da der Test nur die Klasse zu testen, und nicht alles andere.

Dies ist keine DI Primer obwohl, da es ganze Bücher zu dem Thema gewidmet; das obige Beispiel sehr vereinfacht.

Dies ist ein eher „lang“ Thema, aber lassen Sie mich versuchen, es einfach zu setzen.

Eine Schnittstelle ist -as „sie nennen es“ - einen Vertrag. Aber vergessen Sie das Wort.

Der beste Weg, um sie zu verstehen, ist durch eine Art von Pseudo-Codebeispiel. Das ist, wie ich sie lange Zeit verstanden vor.

Angenommen, Sie eine App haben, die Nachrichten verarbeitet. Eine Nachricht enthält einige Sachen, wie ein Gegenstand, ein Text, etc.

So schreiben Sie Ihre MessageController eine Datenbank zu lesen und Nachrichten zu extrahieren. Es ist sehr schön, bis Sie plötzlich hören, dass Faxe werden auch bald umgesetzt werden. So werden Sie nun lesen „Faxe“ und verarbeiten sie als Nachrichten!

Dies könnte leicht in einen Spagetti-Code. Also, was Sie tun, anstatt eine MessageController als Kontrollen „Nachrichten“ des Habens nur, Sie es in der Lage zu machen mit einer Schnittstelle arbeiten genannt IMessage (die ich nur gemeinsame Nutzung, aber nicht erforderlich).

Ihre IMessage-Schnittstelle enthält einige grundlegende Daten, die Sie Notwendigkeit , um sicherzustellen, dass Sie die Nachricht als solche zu verarbeiten sind in der Lage.

Also, wenn Sie Ihre E-Mail erstellen, Fax, Phonecall-Klassen, die Sie machen sie Implement Schnittstelle Namen IMessage .

Also in Ihrem MessageController, können Sie eine Methode wie folgt aufgerufen:

private void ProcessMessage(IMessage oneMessage)
{
    DoSomething();
}

Wenn Sie Interfaces nicht benutzt hatte, dann würden Sie haben müssen:

private void ProcessEmail(Email someEmail);
private void ProcessFax(Fax someFax);
etc.

Also, durch die Verwendung eines gemeinsam Schnittstelle, die Sie gerade dafür gesorgt, dass die Processmessage Methode in der Lage sein wird, mit ihm zu arbeiten, egal ob es sich um ein Fax war, eine E-Mail einen Anruf, usw.

Warum oder wie

Da die Schnittstelle ein Vertrag , der einige Dinge gibt Sie muss haften (oder implementieren), um der Lage sein, es zu benutzen. Betrachten Sie es als Abzeichen . Wenn Ihr Objekt „Fax“ nicht die IMessage-Schnittstelle hat, dann würden Ihre Process Verfahren nicht in der Lage sein, mit dem arbeiten, wird es Ihnen einen ungültigen Typen geben, weil Sie ein Fax auf ein Verfahren vorbei sind, die eine IMessage erwartet Objekt.

Sehen Sie den Punkt?

Denken Sie an der Schnittstelle als „Teilmenge“ von Methoden und Eigenschaften, die Sie zur Verfügung haben werden, trotz des realen Objekttypen. Wenn das ursprüngliche Objekt (Fax, E-Mail, Phonecall, etc.) diese Schnittstelle implementiert, können Sie es über Methoden übergeben Sicherheit, dass die Schnittstelle benötigen.

Es gibt mehr Magie in die dort versteckt sind, können Sie die Schnittstellen zu ihren ursprünglichen Objekte CAST zurück:

Fax MyFax = (Fax) SomeIMessageThatIReceive;

Die Arraylist () in .NET 1.1 hatte eine schöne Schnittstelle namens IList. Wenn Sie eine IList (sehr „generic“) hatte man es in einem Arraylist verwandeln könnte:

ArrayList ar = (ArrayList)SomeIList;

Und es gibt Tausende von Proben dort in der Wildnis.

Schnittstellen wie ISortable, IComparable usw. definieren die Methoden und Eigenschaften, die Sie auf muss in Ihrer Klasse implementieren, um diese Funktionalität zu erreichen.

unsere Probe zu erweitern, könnten Sie eine Liste haben <> von E-Mail, Fax, Phonecall, alle in der gleichen Liste, wenn der Typ IMessage ist, aber man kann sich nicht alle zusammen haben, wenn die Objekte einfach E-Mail waren, Fax, etc.

Wenn Sie wollen sortieren (zum Beispiel oder aufzählen) Ihre Objekte, würden Sie sie brauchen die entsprechende Schnittstelle zu implementieren. In .NET Beispiel, wenn Sie eine Liste von "Fax" Objekte und in der Lage sein wollen sort sie mit Hilfe MyList.Sort (), können Sie auf Notwendigkeit zu machen Ihre Fax-Klasse wie folgt:

public class Fax : ISorteable 
{
   //implement the ISorteable stuff here.
}

Ich hoffe, das Ihnen einen Hinweis gibt. Andere Benutzer werden möglicherweise andere gute Beispiele stellen. Viel Glück! und Umfassen Sie die Kraft der Schnittstellen.

Warnung : Nicht alles, was über Schnittstellen gut ist, gibt es einige Probleme mit ihnen, OOP Puristen werden, einen Krieg zu diesem Thema beginnen. Ich werde beiseite bleiben. Ein Nachteil eines INTERFCE (in .NET 2.0 zumindest) ist, dass Sie nicht PRIVATE Mitglieder haben kann, oder geschützt, es muss Öffentlich sein. Dies macht einen Sinn, aber manchmal wünscht man sich einfach Sachen als privat oder geschützt erklären könnte.

Neben den Funktionsschnittstellen in Programmiersprachen, sie sind auch ein leistungsfähiges Werkzeug, wenn semantische Designideen für andere Menschen zum Ausdruck .

Eine Codebasis mit gut gestalteten Schnittstellen ist plötzlich viel einfacher zu diskutieren. „Ja, Sie brauchen einen CredentialsManager neuen Remote-Server zu registrieren.“ "Pass eine PropertyMap ThingFactory eine Arbeits Instanz zu erhalten."

Die Fähigkeit, eine komplexe Sache mit einem einzigen Wort zu adressieren ist ziemlich nützlich.

Schnittstellen können Sie Code für Objekte in allgemeiner Weise. Zum Beispiel, sagen Sie eine Methode, die Berichte aussendet. Jetzt sagen Sie, eine neue Anforderung, die in kommt, wo Sie brauchen einen neuen Bericht zu schreiben. Es wäre schön, wenn Sie die Methode wiederverwenden könnten Sie schon richtig geschrieben hatte? Schnittstellen macht das einfach:

interface IReport
{
    string RenderReport();
}

class MyNewReport : IReport
{
    public string RenderReport()
    {
        return "Hello World Report!";

    }
}

class AnotherReport : IReport
{
    public string RenderReport()
    {
        return "Another Report!";

    }
}

//This class can process any report that implements IReport!
class ReportEmailer()
{
     public void EmailReport(IReport report)
     {
         Email(report.RenderReport());
     }
}

class MyApp()
{
    void Main()
    {
        //create specific "MyNewReport" report using interface
        IReport newReport = new MyNewReport();

        //create specific "AnotherReport" report using interface
        IReport anotherReport = new AnotherReport();

        ReportEmailer reportEmailer = new ReportEmailer();

        //emailer expects interface
        reportEmailer.EmailReport(newReport);
        reportEmailer.EmailReport(anotherReport);



    }

}

Schnittstellen sind auch der Schlüssel zu Polymorphismus, einer der „drei Säulen OOD“.

Einige Leute oben auf sie berühren, Polymorphismus bedeutet nur eine bestimmte Klasse auf verschiedene „Formen“ annehmen kann. Das heißt, wenn wir zwei Klassen, „Hund“ und „Katze“ und beide implementieren die Schnittstelle „INeedFreshFoodAndWater“ (hehe) - Ihr Code kann so etwas wie dieser (Pseudo-Code) tun:

INeedFreshFoodAndWater[] array = new INeedFreshFoodAndWater[];
array.Add(new Dog());
array.Add(new Cat());

foreach(INeedFreshFoodAndWater item in array)
{
   item.Feed();
   item.Water();
}

Dies ist mächtig, weil es Ihnen erlaubt, verschiedene Klassen von Objekten, abstrakt zu behandeln, und ermöglicht es Ihnen, die Dinge zu tun, wie Ihre Objekte mehr lose gekoppelten, etc. zu machen.

OK, es geht um abstrakte Klassen vs. Schnittstellen ...

Konzeptionell sind abstrakte Klassen dort als Basisklassen verwendet werden. Ziemlich oft sie sich bereits einige grundlegende Funktionen bereitstellen, und die Unterklassen haben ihre eigene Implementierung der abstrakten Methoden zur Verfügung zu stellen (das sind die Methoden, die nicht in der abstrakten Basisklasse implementiert).

Schnittstellen sind zur Entkopplung des Client-Code von den Details einer bestimmten Implementierung meist verwendet. Auch, manchmal die Fähigkeit, die Implementierung ohne Änderung des Client-Code macht den Client-Code allgemeineren zu wechseln.

Auf der technischen Ebene ist es schwieriger, die Grenze zwischen abstrakten Klassen und Schnittstellen zu ziehen, weil in einigen Sprachen (zB C ++), gibt es keinen syntaktischen Unterschied, oder weil Sie auch abstrakte Klassen für die Zwecke der Entkopplung oder Verallgemeinerung nutzen könnten . eine abstrakte Klasse als Schnittstelle zu verwenden ist möglich, weil jede Basisklasse per definitionem eine Schnittstelle definiert, die alle ihrer Unterklassen ehren sollen (das heißt, soll es möglich sein, eine Unterklasse statt einer Basisklasse zu verwenden).

Schnittstellen ist eine Möglichkeit, zu erzwingen, dass ein Objekt eine bestimmte Menge an Funktionalität implementiert, ohne Vererbung verwenden zu müssen (was stark gekoppelten Code führt zu anstatt lose gekoppelt, die durch Verwendung von Schnittstellen erreicht werden kann).

Schnittstellen beschreibt die Funktionalität, nicht die Umsetzung.

Die meisten der Schnittstellen, die Sie über gekommen sind eine Sammlung von Methoden und Eigenschaften Signaturen. Irgend jemand, der eine Schnittstelle implementiert, muss Begriffe zu definieren, was überhaupt in der Schnittstelle.

Einfach ausgedrückt: Eine Schnittstelle ist eine Klasse, die definierten Methoden, aber keine Implementierung in ihnen. Im Gegensatz hat eine abstrakte Klasse einige der Methoden implementiert, aber nicht alle.

Denken Sie an eine Schnittstelle als ein Vertrag. Wenn eine Klasse eine Schnittstelle implementiert, wird die Vereinbarung im Wesentlichen die Bedingungen dieses Vertrages zu ehren. Als Verbraucher interessieren Sie sich nur, dass die Objekte, die Sie haben können, ihre vertraglichen Pflichten erfüllen. Ihr Innenleben und Details sind nicht wichtig.

Ein guter Grund für eine Schnittstelle gegen eine abstrakte Klasse in Java ist, dass eine Unterklasse nicht mehr Basisklassen erweitern kann, aber es kann mehrere Schnittstellen implementieren.

Java erlaubt keine Mehrfachvererbung (für sehr gute Gründe, sehen schrecklich Diamant), aber was ist, wenn Sie Ihre Klasse Versorgung mehrere Sätze von Verhalten haben? Sagen Sie bitte jemand wollen, die es zu wissen, verwendet es serialisiert werden kann, und auch, dass sie sich auf dem Bildschirm malen. die Antwort ist, zwei verschiedene Schnittstellen zu implementieren.

Da Schnittstellen keine Implementierung ihrer eigenen und keine Instanz Mitglieder enthält, es ist sicher einige von ihnen ohne Zweideutigkeiten in der gleichen Klasse zu implementieren.

Die Kehrseite ist, dass man die Umsetzung in jeder Klasse einzeln haben muß. Also, wenn Ihre Hierarchie ist einfach und es gibt Teile der Implementierung, die die gleichen für alle Vererbungsklassen verwenden, um eine abstrakte Klasse sein sollte.

Angenommen, Sie zu Schnittstellen in statisch typisierten objektorientierte Sprachen, die primäre Verwendung ist in behaupten, dass Ihre Klasse folgt einen bestimmten Auftrag oder Protokoll sich beziehen.

Angenommen, Sie haben:

public interface ICommand
{
    void Execute();
}
public class PrintSomething : ICommand
{
    OutputStream Stream { get; set; }
    String Content {get; set;}
    void Execute()
    { 
        Stream.Write(content);
    }
}

Jetzt haben Sie eine substituierbar Kommandostruktur. Jede Instanz einer Klasse, die implementiert IExecute können in einer Liste von einer Art gespeichert werden, sagen etwas, das IEnumerable implementiert und Sie können eine Schleife durch das und jedes ausführen, wohl wissend, dass jedes Objekt wird einfach das Richtige tun. Sie können einen Verbundbefehl erstellen Composite durch die Implementierung, die ihre eigene Liste von Befehlen haben zu laufen, oder eine LoopingCommand eine Reihe von Befehlen wiederholt ausführen können, dann werden Sie die meisten einfacher Dolmetscher haben.

Wenn Sie eine Menge von Objekten zu einem Verhalten zu reduzieren, dass sie alle gemeinsam haben, haben Sie vielleicht Ursache eine Schnittstelle zu extrahieren. Auch, manchmal können Sie Schnittstellen verwenden, um Objekte nicht versehentlich auf die Belange dieser Klasse eindringende; zum Beispiel können Sie eine Schnittstelle implementieren, die Kunden abzurufen, anstatt Änderungsdaten in Ihrem Objekt, und haben die meisten Objekte erhalten nur einen Verweis auf die Retrieval-Schnittstelle.

nur erlaubt

Schnittstellen funktioniert am besten, wenn Ihre Schnittstellen relativ einfach sind und einige Annahmen getroffen werden.

das Liskov subsitution Prinzip nachschlagen mehr Sinn daraus zu machen.

Einige statisch typisierten Sprachen wie C ++ unterstützen keine Schnittstellen als First-Class-Konzept, so dass Sie Schnittstellen mit reinen abstrakten Klassen erstellen.

Aktualisieren Da Sie scheinen über abstrakte Klassen vs. Schnittstellen zu fragen, hier ist meine bevorzugte Simplifizierung:

  • Schnittstellen definieren Fähigkeiten und Funktionen.
  • Abstrakte Klassen definieren Kernfunktionalität.

Normalerweise ich einen Auszug Schnittstelle Refactoring, bevor ich eine abstrakte Klasse zu bauen. Ich bin eher eine abstrakte Klasse zu bauen, wenn ich es denke, ein creational Vertrag sein sollte (genauer gesagt, dass eine bestimmte Art von Konstruktor sollte immer von den Unterklassen unterstützt werden). Allerdings benutze ich selten „reine“ abstrakte Klassen in C # / Java. Ich bin viel eher eine Klasse mit mindestens einer Methode enthält, sinnvolles Verhalten zu implementieren und abstrakten Methoden verwenden, um mit dieser Methode namens Vorlage Methoden zu unterstützen. Dann ist die abstrakte Klasse ist eine Basisimplementierung eines Verhaltens, die alle konkreten Subklassen Vorteil, ohne nehmen neu zu implementieren.

Einfache Antwort: Eine Schnittstelle ist ein Bündel von Methodensignaturen (+ Rückgabetyp). Wenn ein Objekt sagt, es Geräte eine Schnittstelle, wissen Sie, es macht die Reihe von Methoden.

Schnittstellen sind eine Möglichkeit, Konventionen in einer Weise zu implementieren, die nach wie vor stark typisiert und polymorph.

Ein gutes Beispiel aus der Praxis wäre IDisposable in .NET. Eine Klasse, die die Schnittstelle implementiert IDisposable Kräfte, die Klasse, um die Entsorgung () Methode zu implementieren. Wenn die Klasse Entsorgen nicht implementiert () Sie einen Compiler-Fehler erhalten, wenn zu bauen versucht. Darüber hinaus ist diese Codemuster:

using (DisposableClass myClass = new DisposableClass())
  {
  // code goes here
  }

Wird verursachen myClass.Dispose () automatisch ausgeführt werden, wenn die Ausführung des inneren Blocks austritt.

Allerdings, und das ist wichtig, gibt es keine Durchsetzung, was Ihre Methode Dispose () tun soll. Sie könnten Ihre Methode Dispose () wählen Zufalls Rezepte aus einer Datei haben und sie an eine Verteilerliste eine E-Mail, der Compiler kümmert sich nicht darum. Die Absicht des IDisposable Muster ist es, Ressourcen zu machen Reinigung einfacher. Wenn Instanzen einer Klasse werden auf Datei halten Griffe dann IDisposable macht es sehr einfach, die Aufhebung der Zuordnung und Bereinigungscode an einem Ort zu zentralisieren und eine Art von Anwendung zu fördern, die gewährleistet, dass Deallokation tritt immer.

Und das ist der Schlüssel zu Schnittstellen. Sie sind eine Art und Weise Programmierkonventionen und Entwurfsmuster zu optimieren. Die, wenn sie richtig eingesetzt, fördert einfacher, selbsterklärend Code, der einfacher zu bedienen ist, einfacher zu halten und mehr korrekt.

Hier ist ein db verwandtes Beispiel habe ich oft verwenden. Lassen Sie uns sagen, dass Sie ein Objekt haben und ein Containerobjekt wie eine Liste. Nehmen wir an, dass irgendwann sollten Sie die Objekte in einer bestimmten Reihenfolge zu speichern. Es sei angenommen, daß die Sequenz nicht auf die Position in dem Array verbunden ist, sondern dass die Objekte sind eine Teilmenge von einer größeren Menge von Objekten und die Sequenzposition ist im Zusammenhang mit den Datenbank SQL-Filtern.

Um den Überblick über Ihre individuelle Sequenz zu halten Positionen, die Sie Ihr Objekt implementieren eine benutzerdefinierte Schnittstelle machen könnte. Die benutzerdefinierte Schnittstelle könnte die organisatorischen Aufwand vermitteln erforderlich ist, um solche Sequenzen zu halten.

Zum Beispiel ist die Sequenz, die Sie interessiert sind, hat nichts mit Primärschlüssel in den Datensätzen zu tun. Mit dem Ziel, die Umsetzung der Schnittstelle könnte man sagen myObject.next () oder myObject.prev ().

Ich habe das gleiche Problem habe, wie Sie und ich die „Vertrag“ Erklärung ein wenig verwirrend finden.

Wenn Sie angeben, dass ein Verfahren nimmt eine IEnumerable-Schnittstelle als In-Parameter, man könnte sagen, dass dies ein Vertrag spezifiziert ist, dass der Parameter von einem Typ sein muss, die von der Schnittstelle IEnumerable erbt und unterstützt somit alle in den IEnumerable angegebenen Methoden Schnittstelle. Das gleiche wäre wahr, aber wenn wir eine abstrakte Klasse oder eine normale Klasse verwendet. Jedes Objekt, das aus diesen Klassen erbt wäre ok als Parameter übergeben in. Sie wäre in der Lage, in jedem Fall zu sagen, dass das geerbte Objekt alle öffentlichen Methoden in der Basisklasse unterstützt, ob die Basisklasse eine normale Klasse, eine abstrakte Klasse oder eine Schnittstelle ist.

Eine abstrakte Klasse mit allen abstrakten Methoden ist grundsätzlich das gleiche wie eine Schnittstelle, so dass Sie eine Schnittstelle sagen könnten, ist einfach eine Klasse ohne implementierten Methoden. Sie könnten tatsächlich Schnittstellen aus der Sprache fallen und nur verwenden abstrakte Klasse mit nur abstrakten Methoden statt. Ich denke, der Grund, warum wir sie trennen semantischen Gründen ist aber aus Gründen Codierung Ich sehe nicht, den Grund und finden es nur verwirrend.

Ein weiterer Vorschlag könnte sein, um die Schnittstelle zu benennen Klasse Schnittstelle wie die Schnittstelle nur eine weitere Variation einer Klasse ist.

In bestimmten Sprachen gibt es feine Unterschiede, die eine Klasse erlauben nur 1 Klasse zu erben, sondern mehrere Schnittstellen, während in anderen Sie viele beide haben könnten, aber das ist ein anderes Thema und nicht direkt im Zusammenhang Ich denke

Die einfachste Möglichkeit, Schnittstellen zu verstehen ist, unter Berücksichtigung, welche Klassenvererbung zu starten bedeutet. Es umfasst zwei Aspekte:

  1. Die Mitglieder einer abgeleiteten Klasse können Mitglieder einer Basisklasse als ihre eigenen verwenden öffentlich oder geschützt.
  2. Die Mitglieder einer abgeleiteten Klasse kann durch Code verwendet werden, die ein Mitglied der Basisklasse erwartet (das heißt, sie ersetzbar sind).

Beide Funktionen sind nützlich, aber, weil es schwierig ist, eine Klasse zu den Mitgliedern erlauben, von mehr als einer Klasse als seine eigene, viele Sprachen und Frameworks verwenden nur Klassen können von einer einzigen Basisklasse erben. Auf der anderen Seite gibt es keine besondere Schwierigkeit bei, die eine Klasse für mehrere andere unabhängige Dinge ersetzbar sein.

Da ferner der erste Vorteil der Vererbung kann weitgehend durch Verkapselung erreicht werden kann, ist der relative Vorteil Mehrfachvererbung des ersten Typs aus der Gewährung des etwas eingeschränkt. Auf der anderen Seite, ist eine nützliche Fähigkeit, in der Lage, ein Objekt für mehrere unabhängigen Arten von Dingen zu ersetzen, die nicht ohne weiteres ohne Sprachunterstützung erreicht werden können.

Schnittstellen bieten ein Mittel, durch die eine Sprache / Rahmenprogramme erlauben kann, aus dem zweiten Aspekt der Vererbung für mehrere Basistypen profitieren, ohne dass es erforderlich ist, um auch die erste zu liefern.

Schnittstelle ist wie eine völlig abstrakte Klasse. Das heißt, eine abstrakte Klasse mit nur abstrakt Mitgliedern. Sie können auch mehrere Schnittstellen implementieren, ist es wie aus mehreren völlig abstrakten Klassen erben. Wie auch immer .. diese Erklärung hilft nur, wenn Sie verstehen, was eine abstrakte Klasse ist.

Wie andere hier schon gesagt haben, definieren Schnittstellen einen Vertrag (wie die Klassen, die die Schnittstelle verwenden „Look“) und abstrakte Klassen definieren gemeinsam genutzte Funktionalität.

Lassen Sie uns sehen, ob der Code hilft:

public interface IReport
{
    void RenderReport(); // This just defines the method prototype
}

public abstract class Reporter
{
    protected void DoSomething()
    {
        // This method is the same for every class that inherits from this class
    }
}

public class ReportViolators : Reporter, IReport
{
    public void RenderReport()
    {
        // Some kind of implementation specific to this class
    }
}

public class ClientApp
{
    var violatorsReport = new ReportViolators();

    // The interface method
    violatorsReport.RenderReport();

    // The abstract class method
    violatorsReport.DoSomething();
}

Schnittstellen erfordert jede Klasse, die sie die Methoden in der Schnittstelle definiert enthält implementiert.

Der Zweck ist, so dass, ohne den Code in einer Klasse zu sehen, so können Sie wissen, ob es für eine bestimmte Aufgabe verwendet werden kann. Zum Beispiel implementiert die Klasse Integer in Java die vergleichbare Schnittstelle, so, wenn Sie nur die Methode Header (public class String implementiert Comparable) sahen, würden Sie wissen, dass es eine Methode compareTo () enthält.

In Ihrem einfachen Fall Sie etwas ähnliches erreichen könnten, was Sie mit Schnittstellen erhalten, indem eine gemeinsame Basisklasse verwenden, die show() implementiert (oder vielleicht definiert sich als Zusammenfassung). Lassen Sie mich Ihre generischen Namen ändern, um etwas konkreter, Adler und Hawk statt MyClass1 und MyClass2 . In diesem Fall könnten Sie schreiben Code wie

Bird bird = GetMeAnInstanceOfABird(someCriteriaForSelectingASpecificKindOfBird);
bird.Fly(Direction.South, Speed.CruisingSpeed);

Das können Sie Code schreiben, der alles verarbeiten kann, dass ist ein Vogel . Sie könnten dann Code schreiben, die bewirkt, dass Vogel seine Sache zu tun (fliegen, essen, Eier legen und so weiter), die auf einer Instanz handelt es behandelt wie ein Vogel . Dieser Code würde funktionieren, ob Vogel ist wirklich ein Adler Hawk , oder irgendetwas anderes, das von Vogel ableitet.

Das Paradigma beginnt chaotisch zu bekommen, obwohl, wenn Sie nicht über einen wahres haben ist eine Beziehung. Angenommen, Sie Code schreiben möchten, die Dinge um in den Himmel fliegt. Wenn Sie diesen Code schreiben zu akzeptieren, einen Vogel Basisklasse, es plötzlich hart wird, dass Code zu entwickeln, auf einer Jumbojet Instanz zu arbeiten, denn während ein Vogel und Jumbojet kann sicherlich beide fliegen, ein Jumbojet ist sicherlich nicht ein Vogel .

Geben Sie die Schnittstelle.

Was Vogel (und Adler und Hawk ) tun gemeinsam haben, ist, dass sie alle können fliegen . Wenn Sie den obigen Code zu schreiben, anstatt auf einer Schnittstelle zu handeln, ifly , kann dieser Code auf alles angewendet werden, die eine Implementierung dieser Schnittstelle zur Verfügung stellt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top