2009-06-23 6 views
21

Ich wurde vor ein paar Jahren in ein Software-Projekt bei der Arbeit gezwungen und war gezwungen, C# schnell zu lernen. Mein Programmierhintergrund ist schwach (Classic ASP).Welche Vorteile bietet die Verwendung einer Schnittstelle in C#?

Ich habe über die Jahre ziemlich viel gelernt, aber aufgrund der erzwungenen Art, wie ich C# gelernt habe, gibt es eine Menge grundlegender Konzepte, auf die ich nicht weiß.

Insbesondere eine Schnittstelle. Ich verstehe die Grundlagen, aber wenn ich eine App schreibe, fällt es mir schwer, einen praktischen Nutzen zu finden. Warum sollte man eine Schnittstelle für ihre Anwendung schreiben?

Dank Kevin

+7

Siehe http://stackoverflow.com/questions/56867/interface-vs-base-class und hier http://stackoverflow.com/questions/444245/how-will-i-know-when-to- create-an-interface und eine Suche nach "C# interface" für mehr. – Marc

Antwort

16

Eine Schnittstelle sagt, wie etwas funktionieren sollte. Betrachten Sie es als einen Vertrag oder eine Vorlage. Es ist der Schlüssel zu Dingen wie Inverson Control oder Dependancy Injection.

Ich verwende Strukturkarte als IoC-Container. Dies ermöglicht mir, eine Schnittstelle für alle meine Klassen zu definieren. Wo Sie

Widget w = new Widget(); 

sagen könnte würde ich sagen,

IWidget w = ObjectFactory.GetInstance<IWidget>(); 

Dies ist sehr mächtig, dass mein Code sagt, nicht unbedingt, was wirklich ein Widget ist. Es weiß nur, was ein Widget tun kann, basierend auf der Schnittstelle von IWidget.

Dies hat einige große Macht, dass jetzt, wo ich einen IoC-Container verwende ich ein paar raffinierte Dinge tun kann. In meinen Unit-Tests, wo ich ein Widget verwenden muss, kann ich einen Mock für Widget erstellen. Sagen wir also, dass mein Widget durch die Verbindung mit einer Datenbank oder einem Webdienst etwas sehr Leistungsfähiges tut, kann mein Pseudonym die Verbindung zu diesen Ressourcen simulieren und zu mir zurückgeworfenen Daten zurückkehren. Dadurch läuft mein Test schneller und verhält sich zuverlässiger. Da ich StructureMap benutze, kann ich StructureMap sagen, dass während des produktiven Einsatzes meines Codes und der verspotteten Version des Widgets während des Tests entweder die programmatische oder die Konfiguration die tatsächliche Implementierung meines Widgets laden soll.

Auch weil ich einen IoC-Container verwende, kann ich meiner Anwendung coole neue Funktionen zur Verfügung stellen, wie das Schreiben von drei verschiedenen Möglichkeiten, Daten zwischenzuspeichern. Ich kann einen lokalen Entwickler-Box-Cache verwenden, der ein Tool wie Lucene.NET für einen lokalen Cache verwendet. Ich kann einen Entwicklungsserver verwenden den .NET-Cache, der auf einer Box läuft. Und dann kann ich eine dritte Option für meine Produktionsserver verwenden eine Cache-Schicht wie MemCache Win32 oder Velocity. Solange alle drei Caching-Implementierungen der gleichen Schnittstelle entsprechen, betrifft ihre tatsächliche Implementierung mich (oder meinen Code) überhaupt nicht. Ich frage einfach StructureMap, um die aktuelle Umgebung zu holen und dann zur Arbeit zu gehen.

Wenn Sie Dependency Injection überhaupt verfolgen, dann sind Schnittstellen auch hier mit einem IoC-Container wie StructureMap praktisch, da ich die Verwendung einer Klasse über ein Interface im Konstruktor meiner Klasse deklarieren kann.

public class Widget(IWidgetRepository repository, IWidgetService service) : IWidget 
{ 
    //do something here using my repository and service 
} 

Und dann, wenn ich neue Instanz von Widget haft StructureMap wie dieser

IWidget widget = ObjectFactory.GetInstance<IWidget>(); 

Hinweis, dass ich bin nicht das Repository oder eine Dienstleistung im Konstruktor angeben. StructureMap weiß über die im Konstruktor angegebenen Schnittstellen, wie man die passenden Instanzen bekommt und diese auch übergibt. Dies macht sehr starken und sauberen Code!

Alles von der einfachen Definition von Schnittstellen und einige kluge Verwendung von ihnen!

0

guter Artikel.

Eine Schnittstelle ist ein Vertrag, der einem Client garantiert, wie sich eine Klasse oder Struktur verhält.

http://www.codeguru.com/csharp/csharp/cs_syntax/interfaces/article.php/c7563

Dies könnte die klarste einfachste Weg, zu erklären sein, dass ich über gekommen sind:

„Die Antwort ist, dass sie eine ziemlich typsicher mittels Gebäude Routinen zur Verfügung stellen, die Objekte, wenn Sie don akzeptieren Ich kenne den spezifischen Objekttyp, der im Voraus übergeben wird, und weiß nur, dass die Objekte, die an deine Routine übergeben werden, bestimmte Mitglieder haben, die für deine Routine vorhanden sein müssen, damit sie mit ihr arbeiten können Dieses Objekt Das beste Beispiel, das ich für die Notwendigkeit von Schnittstellen geben kann, ist eine Teamumgebung, in der Schnittstellen definiert sind, wie unterschiedliche Komponenten aussehen ts sprechen miteinander. Indem Sie eine Schnittstelle verwenden, eliminieren Sie die Möglichkeit, dass ein Entwickler falsch interpretiert, welche Elemente sie zu einem Typ hinzufügen müssen oder wie sie einen anderen Typ aufrufen, der eine Schnittstelle definiert. Ohne eine Schnittstelle schleichen sich Fehler in das System ein und erscheinen erst zur Laufzeit, wenn sie schwer zu finden sind. Mit Schnittstellen, Fehler in einer Art definieren, werden sofort zum Zeitpunkt der Kompilierung gefangen, wo die Kosten viel geringer.“

7

Der grundlegende Fall ist der‚iWriter‘Fall.

Angenommen, Sie eine Klasse machen, die schreiben können an die Konsole, und es hat alle Arten von nützlichen Funktionen wie write() und peek().

Dann möchten Sie eine Klasse schreiben, die auf den Drucker schreiben kann, also statt eine neue Klasse neu zu erfinden, verwenden Sie die IWriter-Schnittstelle

Jetzt ist die coole Sache über Schnittstellen, dass Sie Ihren ganzen Schreibcode schreiben können, ohne zu wissen, was ist Ihr Schreibziel im Voraus und kann dann, wenn der Benutzer (zur Laufzeit) entscheidet, ob er auf die Konsole oder den Drucker schreiben möchte, das Objekt nur als Konsolen-/Druckerschreiber definieren und Sie nichts ändern müssen Dein Schreibcode, weil beide das gleiche Frontend (Interface) benutzen.

0

Hier ist eine book, die über Schnittstellen spricht. Es fördert die Vorstellung, dass Interfaces zum Client gehören, also zum Aufrufer. Es ist eine nette Idee. Wenn Sie nur das Ding brauchen, das Sie aufrufen, um zu implementieren - sagen wir - count() und get(), dann können Sie eine solche Schnittstelle definieren und Klassen diese Funktionen implementieren lassen. Einige Klassen werden viele andere Funktionen haben, aber Sie sind nur an diesen beiden interessiert - Sie müssen also weniger über die Klassen wissen, mit denen Sie arbeiten. Solange sie den Vertrag erfüllen, können Sie sie verwenden.

1

Ein Beispiel. Betrachten Sie eine MDI-Anwendung, die Berichte anzeigt, es gibt grundsätzlich zwei verschiedene Berichtstypen. Ein Diagramm und ein Raster. Ich muss diese Berichte als PDF speichern und muss sie an jemanden senden. Der Event-Handler für das Menü klickt der Benutzer einen Bericht speichern PDF dies tun könnte:

void ExportPDF_Clicked(...) { 
    if(currentDocument is ChartReport) { 
     ChartReport r = currentDocument as ChartReport; 
     r.SavePDF(); 
    } else if(currentDocument is GridReport) { 
    GridReport r = currentDocument as GridReport; 
     r.SavePDF(); 
    } 
} 

ich lieber meinen ChartReport machen würde und GridReport diese Schnittstelle implementieren:

public interface Report { 
    void MailTo(); 
    void SavePDF(); 
} 

Jetzt kann ich tun:

void ExportPDF_Clicked(...) { 
    Report r = currentDocument as Report; 
    r.SavePDF(); 
} 

Ähnliche für andere Code, der die gleiche Operation tun müssen (in einer Datei speichern, zoomen, drucken, etc.) zu den verschiedenen Berichtsarten. Der obige Code funktioniert immer noch gut, wenn ich einen PivotTableReport hinzufüge, der auch Rpoert in der nächsten Woche antreibt.

1

IOC und Dependency-Injektion wurden bereits oben erwähnt, und ich möchte Sie auffordern, sie zu betrachten.

In vielen Fällen können Schnittstellen jedoch einen Vertrag für ein Objekt angeben, für das kein Vererbungsmodell erforderlich ist.

Sagen wir, ich habe Klasse Foo, das hat Funktionen x und y und Eigenschaft z, und ich baue meinen Code darum herum.

Wenn ich einen besseren Weg finde, um Foo zu machen, oder eine andere Art von Foo erfordert Implementierung, kann ich natürlich eine Basis Foo Klasse auf FooA, FooB, MyFoo etc erweitern, aber das würde erfordern, dass alle Foos die haben gleiche Kernfunktionalität, oder in der Tat, dass alle zukünftigen Foo-Ersteller Zugriff auf die Basis-Foo-Klasse haben und ihre internen Abläufe verstehen. In C# würde das bedeuten, dass zukünftige Foos nichts anderes als Foo erben können, da C# keine Mehrfachvererbung unterstützt.

Es würde auch erfordern, dass ich mir möglicher zukünftiger Zustände von Foo bewusst bin, und versuche, sie nicht in meiner Basis-Foo-Klasse zu hemmen.

Mithilfe einer Schnittstelle gibt IFoo einfach den 'Vertrag' an, den eine Klasse benötigt, um in meinem Foo-Framework zu arbeiten, und es ist mir egal, wie zukünftige Foo-Klassen intern vererben oder aussehen können, solange sie fn haben x Fn y und z. Es macht einen Rahmen viel flexibler und offen für zukünftige Ergänzungen.

Wenn Foo jedoch eine große Menge an Kern an seiner Basis benötigt, um zu arbeiten, die in einem Vertragsszenario nicht anwendbar wäre, dann würden Sie die Vererbung bevorzugen.

6

Eine einfache Antwort: Verwenden Schnittstellen programmieren gegen den Vertrag statt der Umsetzung.

Wie könnte das möglicherweise helfen? Die Verwendung von Interfaces wird (hoffentlich) dazu führen, dass Sie Klassen lockerer koppeln. Wenn Sie gegen Ihre eigenen konkreten Klassen kodieren, ist es leicht, die Datenstrukturen zu stochern, ohne dass strikte Trennung der Probleme erfolgt. Sie enden mit Klassen, die alles über die anderen Klassen "wissen" und die Dinge können sich ziemlich verheddern. Indem Sie sich auf eine Schnittstelle beschränken, haben Sie nur die Sicherheit, dass sie den Vertrag der Schnittstelle erfüllt. Es injiziert eine manchmal hilfreiche Reibung gegen eine enge Kopplung.

0

Ein paar Dinge, wenn Sie von einer Schnittstelle erben, zwingt Sie Sie, alle Methoden zu implementieren, die in der Schnittstelle definiert sind. Zum anderen ist dies auch eine gute Möglichkeit, Mehrfachvererbung einzubringen, die für normale Klassen nicht unterstützt wird. http://msdn.microsoft.com/en-us/library/ms173156.aspx

0

Einfache Antwort basierend auf ersten Prinzipien:

Ein Programm ist ein Universum mit seiner eigenen Metaphysik (die Realität/Stoff/Stoff, aus dem Code) und Epistemologie (was Sie/glauben/Grund, über das wissen können, Code). Eine gute Programmiersprache versucht, die metaphysische Flexibilität zu maximieren (lässt Sie die Dinge leicht machen), während epistemische Strenge gewährleistet wird (stellt sicher, dass Ihr Universum intern konsistent ist).

Denken Sie also an die Implementationsvererbung als metaphysischen Baustein (das Zeug, das Ihr kleines Universum von Code ausmacht) und Schnittstellenvererbung als eine epistemische Einschränkung (es erlaubt Ihnen, etwas über Ihren Code zu glauben).

Sie verwenden Schnittstellen, wenn Sie nur sicherstellen möchten, dass Sie etwas glauben können. Meistens ist das alles, was Sie brauchen.

0

Sie haben erwähnt, dass es schwierig ist, eine praktische Verwendung für Schnittstellen zu finden. Ich habe festgestellt, dass sie beim Erstellen erweiterbarer Anwendungen zum Tragen kommen, z. B. eine Plugin-basierte App, bei der ein Drittanbieter-Plugin bestimmten Regeln entsprechen muss. Diese Regeln können über eine Schnittstelle definiert werden.

Sie könnten es so machen, dass wenn das Plugin geladen wird, muss es eine Init-Methode haben, die eine Klasse übernimmt, die die IServices-Schnittstelle implementiert.

public interface IServices 
{ 
    DataManager Data { get; set; } 
    LogManager Log { get; set; } 
    SomeOtherManager SomeOther { get; set; } 
} 

public class MrPlugin 
{ 
    public void Init(IServices services) 
    { 
     // Do stuff with services 
    } 
} 

Also .. Wenn Sie eine Klasse, die die IServices-Schnittstelle implementiert, und man es dann einmal instanziiert, können Sie es zu allen Plugins bei der Initialisierung übergeben und können sie verwenden, was Dienstleistungen, die Sie in der Schnittstelle definiert haben .

Verwandte Themen