Woher weiß ich, wann ich eine Schnittstelle erstellen muss?


196

Ich bin an einem Punkt in meinem Entwicklungslernen angelangt, an dem ich das Gefühl habe, mehr über Schnittstellen lernen zu müssen.

Ich lese häufig über sie, aber es scheint nur, dass ich sie nicht erfassen kann.

Ich habe Beispiele gelesen wie: Tierbasisklasse mit einer minimalen Schnittstelle für Dinge wie 'Walk', 'Run', 'GetLegs' usw. - aber ich habe noch nie an etwas gearbeitet und dachte: "Hey, ich sollte eine Schnittstelle verwenden Hier!"

Was vermisse ich? Warum ist es für mich so schwer zu verstehen! Ich bin nur eingeschüchtert von der Tatsache, dass ich möglicherweise nie einen konkreten Bedarf für einen erkennen werde - hauptsächlich aufgrund eines fehlenden Aspekts, um sie zu verstehen! Ich habe das Gefühl, dass mir als Entwickler etwas ganz oben fehlt! Wenn jemand eine solche Erfahrung gemacht und einen Durchbruch erzielt hat, würde ich mich über einige Tipps zum Verständnis dieses Konzepts freuen. Danke dir.


Antworten:


151

es löst dieses konkrete Problem:

Sie haben a, b, c, d von 4 verschiedenen Typen. Überall in Ihrem Code haben Sie so etwas wie:

a.Process();
b.Process();
c.Process();
d.Process();

Warum lassen Sie sie IProcessable nicht implementieren und tun dies dann?

List<IProcessable> list;

foreach(IProcessable p in list)
    p.Process();

Dies lässt sich viel besser skalieren, wenn Sie beispielsweise 50 Arten von Klassen hinzufügen, die alle dasselbe tun.


Ein weiteres konkretes Problem:

Haben Sie sich jemals System.Linq.Enumerable angesehen? Es definiert eine Menge Erweiterungsmethoden, die mit jedem Typ arbeiten, der IEnumerable implementiert. Da alles, was IEnumerable implementiert, im Grunde sagt "Ich unterstütze die Iteration in einem ungeordneten Muster für jeden Typ", können Sie komplexe Verhaltensweisen (Anzahl, Max, Wo, Auswahl usw.) für jeden aufzählbaren Typ definieren.


2
Das hilft. Was ist der Vorteil einer vorhandenen Schnittstelle, anstatt dass die Typen nur alle für die Process () -Methode implementiert sind?
user53885

Sie könnten dieselbe Variable p nur verwenden, wenn sie alle Unterklassen desselben Basistyps wären oder die Schnittstelle implementiert hätten.
Karl

Sie müssen nicht wirken, im Gegensatz zu 50 verschiedenen Klassen mit einer Process-Methode. C # verwendet keine "Ententypisierung". Nur weil A Process () und B Process () hat, bedeutet dies nicht, dass es auch keine generische Möglichkeit zum Aufrufen gibt. Dafür benötigen Sie eine Schnittstelle.
user7116

richtig. Ich habe gerade "var" in "IProcessable" geändert, damit das Beispiel sinnvoller wird.
Jimmy

2
@ Rogerio: Ich habe versucht, generisch zu sein. Der Punkt ist nicht, dass "wenn Sie Dinge haben, die eine Process () - Funktion haben", sondern "wenn Sie Dinge haben, die einen gemeinsamen Satz von Methoden haben". Das Beispiel kann leicht inforeach(IMyCompanyWidgetFrobber a in list) a.Frob(widget, context);
Jimmy

133

Ich mag Jimmys Antwort sehr, aber ich habe das Gefühl, dass ich etwas hinzufügen muss. Der Schlüssel zum Ganzen ist das "fähige" in IProcess fähig. Es gibt eine Fähigkeit (oder Eigenschaft, die jedoch "intrinsische Qualität" bedeutet, nicht im Sinne von C # -Eigenschaften) des Objekts an, das die Schnittstelle implementiert. IAnimal ist wahrscheinlich kein gutes Beispiel für eine Schnittstelle, aber IWalkable ist möglicherweise eine gute Schnittstelle, wenn Ihr System viele Dinge hat, die laufen können. Möglicherweise haben Sie von Tieren abgeleitete Klassen wie Hund, Kuh, Fisch, Schlange. Die ersten beiden würden wahrscheinlich IWalkable implementieren, die beiden letzteren gehen nicht, also würden sie nicht. Jetzt fragen Sie: "Warum nicht einfach eine andere Superklasse haben, WalkingAnimal, von der Hund und Kuh abstammen?". Die Antwort ist, wenn Sie etwas vollständig außerhalb des Vererbungsbaums haben, das auch laufen kann, wie z. B. einen Roboter. Robot würde IWalkable implementieren, aber wahrscheinlich nicht von Animal ableiten. Wenn Sie eine Liste von Dingen wollen, die laufen können,

Ersetzen Sie jetzt IWalkable durch etwas Software-ähnlicheres wie IPersistable, und die Analogie kommt dem, was Sie in einem echten Programm sehen würden, viel näher.


9
Mir gefällt, was Sie sich ausgedacht haben - "Es zeigt eine Fähigkeit an". Schnittstellen werden wirklich benötigt, wenn Sie Fähigkeiten definieren müssen, da die Grundlagen an der "Basis" -Klasse festhalten müssen.
Ramiz Uddin

71

Verwenden Sie Schnittstellen, wenn sich Implementierungen derselben Funktionalität unterscheiden.

Verwenden Sie eine Zusammenfassung / Basisklasse, wenn Sie eine gemeinsame konkrete Implementierung gemeinsam nutzen möchten.


8
Der erste heißt Polymorphismus. Das zweite ist Schlangenöl - es sei denn, die Unterklasse ist eine Grundklasse (es gibt keine Verletzung des Liskov-Substitutionsprinzips). Sie sollten die Zusammensetzung der Vererbung vorziehen.
Arnis Lapsa

@ArnisLapsa Ich verstehe nicht ganz, was du mit "es sei denn, Unterklasse ist Grundklasse" meinst. Wann wäre eine Unterklasse keine Basisklasse? (wie im isSchlüsselwort)
März 2377

32

Stellen Sie sich eine Schnittstelle wie einen Vertrag vor. Es ist eine Möglichkeit zu sagen: "Diese Klassen sollten diesen Regeln folgen."

Im IAnimal-Beispiel kann man also sagen: "Ich muss Run, Walk usw. für Klassen aufrufen können, die IAnimal implementieren."

Warum ist das nützlich? Möglicherweise möchten Sie eine Funktion erstellen, die auf der Tatsache beruht, dass Sie beispielsweise Run and Walk für das Objekt aufrufen können müssen. Sie könnten Folgendes haben:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

... und wiederholen Sie dies für alle Objekte, von denen Sie wissen, dass sie laufen und gehen können. Mit Ihrer IAnimal-Schnittstelle können Sie die Funktion jedoch einmal wie folgt definieren:

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

Wenn Sie gegen die Schnittstelle programmieren, vertrauen Sie im Wesentlichen darauf, dass die Klassen die Absicht der Schnittstelle implementieren. In unserem Beispiel lautet der Gedanke also: "Es ist mir egal, wie sie laufen und gehen, solange sie laufen und gehen. Mein RunThenWalk ist gültig, solange sie diese Vereinbarung erfüllen. Es funktioniert einwandfrei, ohne etwas anderes zu wissen." die Klasse."

Es gibt auch eine gute Diskussion in dieser verwandten Frage .


1
Vergessen Sie nicht die Unabhängigkeit der Implementierung. Ein Interface ermöglicht es einem, sich nicht darum zu kümmern, wie die zugrunde liegende Funktion implementiert wird, sondern nur darum, was das Interface sagt.
Matthew Brubaker

18

Mach dir nicht so viele Sorgen. Viele Entwickler müssen selten eine Schnittstelle schreiben. Sie werden häufig Schnittstellen verwenden, die im .NET Framework verfügbar sind. Wenn Sie jedoch nicht das Bedürfnis haben, bald eine zu schreiben, ist dies nicht überraschend.

Das Beispiel, das ich immer jemandem gebe, ist, wenn Sie eine Segelboot- und eine Viper-Klasse haben. Sie erben die Bootsklasse bzw. die Autoklasse. Angenommen, Sie müssen alle diese Objekte durchlaufen und ihre Drive()Methode aufrufen . Sie könnten zwar Code wie den folgenden schreiben:

if(myObject is Boat)
    ((Boat)myObject).Drive()
else
    if (myObject is Car)
        ((Car)myObject).Drive()

Es wäre viel einfacher zu schreiben:

((IDrivable)myObject).Drive()

16

Jimmy hat es richtig gemacht, wenn Sie eine einzelne Variable für mehrere Typen verwenden möchten, aber alle diese Typen dieselbe Methode über eine Schnittstellendeklaration implementieren. Dann können Sie sie als Hauptmethode für die vom Typ Schnittstelle eingegebene Variable aufrufen.

Es gibt jedoch einen zweiten Grund, Schnittstellen zu verwenden. Wenn der Projektarchitekt eine andere Person als der Implementierungscodierer ist oder mehrere Implementierungscodierer und ein Projektmanager vorhanden sind. Die verantwortliche Person kann eine ganze Reihe von Schnittstellen schreiben und sicherstellen, dass das System zusammenarbeitet, und es dann den Entwicklern überlassen, die Schnittstellen mit Implementierungsklassen auszufüllen. Dies ist der beste Weg, um sicherzustellen, dass mehrere Personen kompatible Klassen schreiben und dies parallel tun können.


15

Ich mag Armee-Analogie.

Sergeant ist es egal, ob Sie Softwareentwickler , Musiker oder Anwalt sind .
Sie werden als Soldat behandelt .

uml

Es ist für Sergeant einfacher, sich nicht um bestimmte Details von Personen zu kümmern, mit denen er arbeitet,
alle als Soldatenabstraktionen zu behandeln (... und sie zu bestrafen, wenn sie sich nicht wie solche verhalten).

Die Fähigkeit von Personen, sich wie Soldaten zu verhalten, wird als Polymorphismus bezeichnet.

Schnittstellen sind Softwarekonstruktionen, mit denen Polymorphismus erreicht werden kann.

Die Notwendigkeit , Details zu abstrahieren, um Einfachheit zu erreichen, ist die Antwort auf Ihre Frage.

Polymorphismus , der etymologisch "viele Formen" bedeutet, ist die Fähigkeit, ein Objekt einer beliebigen Unterklasse einer Basisklasse so zu behandeln, als wäre es ein Objekt der Basisklasse. Eine Basisklasse hat daher viele Formen: die Basisklasse selbst und jede ihrer Unterklassen.

(..) Dies erleichtert das Schreiben Ihres Codes und das Verstehen anderer. Es macht Ihren Code auch erweiterbar, da andere Unterklassen später zur Typenfamilie hinzugefügt werden könnten und Objekte dieser neuen Unterklassen auch mit dem vorhandenen Code funktionieren würden.


14

Nach meiner Erfahrung trat die treibende Kraft beim Erstellen von Schnittstellen erst auf, als ich mit Unit-Tests mit einem spöttischen Framework begann. Es wurde sehr deutlich, dass die Verwendung von Schnittstellen das Verspotten erheblich erleichtern würde (da das Framework von den virtuellen Methoden abhing). Als ich anfing, sah ich den Wert, die Schnittstelle zu meiner Klasse von der Implementierung zu abstrahieren. Auch wenn ich keine tatsächliche Schnittstelle erstelle, versuche ich jetzt, meine Methoden virtuell zu machen (Bereitstellung einer impliziten Schnittstelle, die überschrieben werden kann).

Es gibt viele andere Gründe, die ich gefunden habe, um die bewährte Methode des Refactorings von Schnittstellen zu verstärken, aber das Testen / Verspotten von Einheiten war der Grund für den ersten "Aha-Moment" praktischer Erfahrung.

EDIT : Zur Verdeutlichung habe ich beim Testen und Verspotten von Einheiten immer zwei Implementierungen - die reale, konkrete Implementierung und eine alternative Scheinimplementierung, die beim Testen verwendet wird. Sobald Sie zwei Implementierungen haben, wird der Wert der Schnittstelle offensichtlich - behandeln Sie sie in Bezug auf die Schnittstelle, damit Sie die Implementierung jederzeit ersetzen können. In diesem Fall ersetze ich es durch eine Scheinschnittstelle. Ich weiß, dass ich dies ohne eine tatsächliche Schnittstelle tun kann, wenn meine Klasse richtig aufgebaut ist, aber die Verwendung einer tatsächlichen Schnittstelle verstärkt dies und macht sie sauberer (für den Leser klarer). Ohne diesen Impuls hätte ich den Wert von Schnittstellen nicht geschätzt, da die meisten meiner Klassen nur eine einzige konkrete Implementierung haben.


Richtig aus falschen Gründen. In Ihrem Fall - Sie erhalten so genannte "Header-Schnittstellen" und zusätzliche Komplexität. Übergewichte erzielen Einfachheit.
Arnis Lapsa

@Arnis - Unit-Tests sind ein "falscher Grund". Das einfache Verspotten von Klassen zum Entfernen von Abhängigkeiten in Tests ist ein "falscher Grund". Entschuldigung, aber ich bin anderer Meinung.
Tvanfosson

1
Tests sollten das Design indirekt beeinflussen, indem sie Feedback geben, ob Code testbar ist oder nicht. Das Hinzufügen von Erweiterungspunkten zur Verbesserung der Testbarkeit selbst ist wie Betrug. Ich denke, Mark Seeman fasst es am besten bit.ly/esi8Wp
Arnis Lapsa

1
@Arnis - Verwenden Sie bei Ihren Unit-Tests überhaupt Mocks? Wenn nicht, wie entfernen Sie die Abhängigkeit von Abhängigkeiten? Verwenden Sie DI überhaupt? Unit-Tests haben mich dazu gebracht, Mocking und DI zu verwenden. Mocking und DI haben bewiesen, dass es sich lohnt, Schnittstellen zu verwenden, um Verträge so zu definieren, wie es kein akademisches Verständnis jemals könnte. Aufgrund meiner Einführung von TDD ist mein Code viel weniger gekoppelt als sonst. Ich denke das ist eine gute Sache.
Tvanfosson

nur zu sagen, dass eine Zersetzung, die nicht entlang sogenannter natürlicher Gelenke erfolgt, zu geringem Zusammenhalt und unnötiger Komplexität führt.
Arnis Lapsa

10

Einige Beispiele ohne Programmierung, anhand derer Sie möglicherweise die geeignete Verwendung von Schnittstellen bei der Programmierung erkennen können.

Es gibt eine Schnittstelle zwischen elektrischen Geräten und dem Stromnetz - es sind die Konventionen über die Form der Stecker und Buchsen und die Spannungen / Ströme über ihnen. Wenn Sie ein neues elektrisches Gerät implementieren möchten, kann Ihr Stecker Dienste aus dem Netzwerk abrufen, solange er den Regeln entspricht. Dies macht die Erweiterbarkeit sehr einfach und reduziert oder senkt die Koordinierungskosten : Sie müssen den Stromversorger nicht über die Funktionsweise Ihres neuen Geräts informieren und eine separate Vereinbarung über den Anschluss Ihres neuen Geräts an das Netzwerk treffen.

Länder haben Standard-Schienenlehren. Dies ermöglicht eine Arbeitsteilung zwischen Ingenieurbüros, die Schienen abstellen, und Ingenieurbüros, die Züge bauen, um auf diesen Schienen zu fahren, und ermöglicht es Eisenbahnunternehmen , Züge zu ersetzen und zu verbessern , ohne das gesamte System neu zu gestalten.

Der Service, den ein Unternehmen einem Kunden bietet, kann als Schnittstelle bezeichnet werden: Eine gut definierte Schnittstelle betont den Service und verbirgt die Mittel . Wenn Sie einen Brief in eine Mailbox legen, erwarten Sie, dass das Postsystem den Brief innerhalb einer bestimmten Zeit zustellt, aber Sie haben keine Erwartungen an die Zustellung des Briefes: Sie müssen es nicht wissen , und der Postdienst hat die Flexibilität dazu Wählen Sie das Versandmittel, das den Anforderungen und den aktuellen Umständen am besten entspricht. Eine Ausnahme bildet die Möglichkeit der Kunden, Luftpost zu wählen - dies ist nicht die Art von Schnittstelle, die ein moderner Computerprogrammierer entworfen hätte, da sie zu viel von der Implementierung enthüllt.

Beispiele aus der Natur: Ich bin nicht besonders scharf auf die Beispiele eat (), makeSound (), move () usw. Sie beschreiben zwar das richtige Verhalten, aber sie beschreiben keine Interaktionen und wie sie aktiviert sind . Die offensichtlichen Beispiele für Schnittstellen, die Interaktionen in der Natur ermöglichen, betreffen die Fortpflanzung. Beispielsweise bietet eine Blume einer Biene eine bestimmte Schnittstelle, damit eine Bestäubung stattfinden kann.


5

Es ist durchaus möglich, Ihr ganzes Leben als .net-Entwickler zu verbringen und niemals Ihre eigenen Schnittstellen zu schreiben. Immerhin haben wir jahrzehntelang ohne sie gut überlebt und unsere Sprachen waren immer noch Turing-vollständig.

Ich kann Ihnen nicht sagen, warum Sie Schnittstellen benötigen, aber ich kann Ihnen eine Liste geben, wo wir sie in unserem aktuellen Projekt verwenden:

  1. In unserem Plug-In-Modell laden wir Plug-Ins nach Schnittstelle und stellen diese Schnittstelle Plug-In-Writern zur Verfügung.

  2. In unserem maschinellen Nachrichtensystem implementieren alle Nachrichtenklassen eine bestimmte Schnittstelle und werden über die Schnittstelle "entpackt".

  3. Unser Konfigurationsmanagementsystem definiert eine Schnittstelle zum Festlegen und Abrufen von Konfigurationseinstellungen.

  4. Wir haben eine Schnittstelle, die wir verwenden, um ein unangenehmes Zirkelverweisproblem zu vermeiden. (Tun Sie dies nicht, wenn Sie nicht müssen.)

Ich denke, wenn es eine Regel gibt, ist es, Schnittstellen zu verwenden, wenn Sie mehrere Klassen innerhalb einer is-a-Beziehung gruppieren möchten, aber keine Implementierung in der Basisklasse bereitstellen möchten.


5

Ein Codebeispiel (Kombination von Andrews mit einem Extra von mir an dem Zweck der Schnittstellen ), in dem auch erläutert wird, warum die Schnittstelle anstelle einer abstrakten Klasse für Sprachen ohne Unterstützung für Mehrfachvererbung (c # und Java):

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

Beachten Sie, dass FileLogger und DataBaseLogger die Schnittstelle nicht benötigen (möglicherweise eine abstrakte Logger-Basisklasse). Bedenken Sie jedoch, dass Sie einen Drittanbieter-Logger verwenden müssen, der Sie zur Verwendung einer Basisklasse zwingt (beispielsweise werden geschützte Methoden verfügbar gemacht, die Sie verwenden müssen). Da die Sprache keine Mehrfachvererbung unterstützt, können Sie den abstrakten Basisklassenansatz nicht verwenden.

Fazit: Verwenden Sie nach Möglichkeit eine Schnittstelle, um zusätzliche Flexibilität für Ihren Code zu erhalten. Ihre Implementierung ist weniger gebunden, sodass sie sich besser für Änderungen eignet.


4

Ich habe ab und zu Schnittstellen verwendet und hier ist meine neueste Verwendung (Namen wurden verallgemeinert):

Ich habe eine Reihe von benutzerdefinierten Steuerelementen in einer WinForm, die Daten in meinem Geschäftsobjekt speichern müssen. Ein Ansatz besteht darin, jedes Steuerelement separat aufzurufen:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

Das Problem bei dieser Implementierung ist, dass ich jedes Mal, wenn ich ein Steuerelement hinzufüge, in meine Methode "Daten speichern" gehen und das neue Steuerelement hinzufügen muss.

Ich habe meine Steuerelemente geändert, um eine ISaveable-Schnittstelle mit der Methode SaveToBusinessObject (...) zu implementieren. Daher durchläuft meine Methode "Daten speichern" jetzt nur noch die Steuerelemente. Wenn sie eine ISaveable-Methode findet, ruft sie SaveToBusinessObject auf. Wenn nun ein neues Steuerelement benötigt wird, muss nur noch ISaveable in diesem Objekt implementiert werden (und niemals eine andere Klasse berühren).

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

Der häufig nicht realisierte Vorteil von Schnittstellen besteht darin, dass Sie Änderungen lokalisieren. Einmal definiert, ändern Sie selten den Gesamtfluss einer Anwendung, nehmen jedoch häufig Änderungen auf Detailebene vor. Wenn Sie die Details in bestimmten Objekten behalten, wirkt sich eine Änderung in ProcessA nicht auf eine Änderung in ProcessB aus. (Basisklassen bieten Ihnen auch diesen Vorteil.)

EDIT: Ein weiterer Vorteil ist die Spezifität der Aktionen. Wie in meinem Beispiel möchte ich nur die Daten speichern. Es ist mir egal, um welche Art von Steuerelement es sich handelt oder ob es etwas anderes kann - ich möchte nur wissen, ob ich die Daten im Steuerelement speichern kann. Es macht meinen Speichercode ziemlich klar - es gibt keine Überprüfungen, ob es sich um Text, Zahlen, Boolesche Werte oder was auch immer handelt, da das benutzerdefinierte Steuerelement all dies übernimmt.


4

Sie sollten eine Schnittstelle definieren, sobald Sie ein Verhalten für Ihre Klasse erzwingen müssen.

Das Verhalten eines Tieres kann Gehen, Essen, Laufen usw. umfassen. Daher definieren Sie sie als Schnittstellen.

Ein weiteres praktisches Beispiel ist die ActionListener-Schnittstelle (oder Runnable-Schnittstelle). Sie würden sie implementieren, wenn Sie ein bestimmtes Ereignis verfolgen müssen. Daher müssen Sie die Implementierung für die actionPerformed(Event e)Methode in Ihrer Klasse (oder Unterklasse) bereitstellen . In ähnlicher Weise stellen Sie für die Runnable-Schnittstelle die Implementierung für die public void run()Methode bereit .

Sie können diese Schnittstellen auch von einer beliebigen Anzahl von Klassen implementieren lassen.

Eine andere Instanz, in der Schnittstellen verwendet werden (in Java), ist die Implementierung der in C ++ angebotenen Mehrfachvererbung.


3
Bitte, Gott, lass sie aufhören, Dinge wie Mehrfachvererbung in Bezug auf Schnittstellen zu sagen. Sie erben KEINE Schnittstelle in einer Klasse. Sie implementieren es.
Andrei Rînea

4

Angenommen, Sie möchten Belästigungen modellieren, die auftreten können, wenn Sie versuchen, einzuschlafen.

Modell vor Schnittstellen

Geben Sie hier die Bildbeschreibung ein

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

Wie Sie deutlich sehen, können viele „Dinge“ ärgerlich sein, wenn Sie versuchen zu schlafen.

Verwendung von Klassen ohne Schnittstellen

Aber wenn es darum geht, diese Klassen zu verwenden, haben wir ein Problem. Sie haben nichts gemeinsam. Sie müssen jede Methode separat aufrufen.

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

Modell mit Schnittstellen

Um dieses Problem zu lösen, können wir ein Iterface einführenGeben Sie hier die Bildbeschreibung ein

interface Annoying{
   public void annoy();

}

Und implementieren Sie es in Klassen

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

Verwendung mit Schnittstellen

Dies erleichtert die Verwendung dieser Klassen erheblich

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}

Ok, aber nicht Neighbourund muss LampJustOutsideYourWindowauch implementieren Annoying?
Stardust

Ja, danke, dass Sie darauf hingewiesen haben. Ich habe eine Änderung mit dieser Änderung vorgenommen
Marcin Szymczak

2

Das einfachste Beispiel ist so etwas wie Zahlungsabwickler (Paypal, PDS usw.).

Angenommen, Sie erstellen eine Schnittstelle IPaymentProcessor mit den Methoden ProcessACH und ProcessCreditCard.

Sie können jetzt eine konkrete Paypal-Implementierung implementieren. Wenn Sie diese Methoden verwenden, rufen Sie PayPal-spezifische Funktionen auf.

Wenn Sie später entscheiden, dass Sie zu einem anderen Anbieter wechseln müssen, können Sie dies tun. Erstellen Sie einfach eine weitere konkrete Implementierung für den neuen Anbieter. Da Sie nur an Ihre Schnittstelle (Vertrag) gebunden sind, können Sie die von Ihrer Anwendung verwendete Schnittstelle austauschen, ohne den Code zu ändern, der sie verwendet.


2

Außerdem können Sie Mock-Unit-Tests (.Net) durchführen. Wenn Ihre Klasse eine Schnittstelle verwendet, können Sie das Objekt in Ihrem Komponententest verspotten und die Logik einfach testen (ohne die Datenbank, den Webdienst usw. zu treffen).

http://www.nmock.org/


2

Wenn Sie die .NET Framework-Assemblys durchsuchen und einen Drilldown in die Basisklassen für eines der Standardobjekte durchführen, werden Sie viele Schnittstellen (Mitglieder mit dem Namen ISomeName) bemerken.

Schnittstellen dienen im Wesentlichen zur Implementierung großer oder kleiner Frameworks. Bei Schnittstellen ging es mir genauso, bis ich ein eigenes Framework schreiben wollte. Ich fand auch heraus, dass das Verständnis von Schnittstellen mir half, Frameworks viel schneller zu lernen. In dem Moment, in dem Sie für fast alles eine elegantere Lösung schreiben möchten, werden Sie feststellen, dass eine Benutzeroberfläche sehr sinnvoll ist. Es ist wie eine Methode, eine Klasse die passende Kleidung für den Job anziehen zu lassen. Noch wichtiger ist, dass Schnittstellen es Systemen ermöglichen, sich viel selbst zu dokumentieren, da komplexe Objekte weniger komplex werden, wenn die Klasse Schnittstellen implementiert, was zur Kategorisierung ihrer Funktionalität beiträgt.

Klassen implementieren Schnittstellen, wenn sie explizit oder implizit an einem Framework teilnehmen möchten. IDisposable ist beispielsweise eine allgemeine Schnittstelle, die eine Methodensignatur für die beliebte und nützliche Dispose () -Methode bereitstellt. In einem Framework müssen Sie oder ein anderer Entwickler nur wissen, dass ((IDisposable) myObject) .Dispose () verfügbar ist, um zu Bereinigungszwecken aufgerufen zu werden, wenn es IDisposable implementiert.

KLASSISCHES BEISPIEL: Ohne Implementierung der IDisposable-Schnittstelle können Sie das Schlüsselwortkonstrukt "using ()" in C # nicht verwenden, da jedes als Parameter angegebene Objekt implizit in IDisposable umgewandelt werden kann.

KOMPLEXES BEISPIEL: Ein komplexeres Beispiel wäre die System.ComponentModel.Component-Klasse. Diese Klasse implementiert sowohl IDisposable als auch IComponent. Die meisten, wenn nicht alle .NET-Objekte, denen ein visueller Designer zugeordnet ist, implementieren IComponent, sodass die IDE mit der Komponente interagieren kann.

SCHLUSSFOLGERUNG: Wenn Sie mit .NET Framework vertraut werden, müssen Sie als Erstes auf eine neue Klasse im Objektbrowser oder im (.NET) Reflector-Tool ( http://www.red-gate.com ) stoßen / products / Reflector / ) überprüft, von welcher Klasse es erbt und welche Schnittstellen es implementiert. .NET Reflector ist sogar noch besser als der Objektbrowser, da Sie damit auch die abgeleiteten Klassen anzeigen können. Auf diese Weise können Sie alle Objekte kennenlernen, die von einer bestimmten Klasse stammen, und so möglicherweise Informationen zu Framework-Funktionen erhalten, von denen Sie nicht wussten, dass sie existieren. Dies ist besonders wichtig, wenn dem .NET Framework aktualisierte oder neue Namespaces hinzugefügt werden.


2

Stellen Sie sich vor, Sie machen ein Ego-Shooter-Spiel. Der Spieler hat mehrere Waffen zur Auswahl.

Wir können eine Schnittstelle haben, Gundie eine Funktion definiert shoot().

Wir brauchen nämlich verschiedene GunKlassenunterklassen ShotGun Sniperund so weiter.

ShotGun implements Gun{
    public void shoot(){
       \\shotgun implementation of shoot.
    } 
}

Sniper implements Gun{
    public void shoot(){
       \\sniper implementation of shoot.
    } 
}

Schützenklasse

Der Schütze hat alle Waffen in seiner Rüstung. Erstellen wir ein List, um es darzustellen.

List<Gun> listOfGuns = new ArrayList<Gun>();

Der Schütze fährt bei Bedarf mit der Funktion durch seine Waffen switchGun()

public void switchGun(){
    //code to cycle through the guns from the list of guns.
    currentGun = //the next gun in the list.
}

Wir können die aktuelle Waffe mit der obigen Funktion einstellen und einfach die shoot()Funktion aufrufen , wenn sie fire()aufgerufen wird.

public void fire(){
    currentGun.shoot();
}

Das Verhalten der Aufnahmefunktion hängt von den verschiedenen Implementierungen der GunSchnittstelle ab.

Fazit

Erstellen Sie eine Schnittstelle, wenn eine Klassenfunktion von einer Funktion einer anderen Klasse abhängig ist, deren Verhalten basierend auf der Instanz (dem Objekt) der implementierten Klasse geändert wird.

Zum Beispiel erwartet die fire()Funktion aus der ShooterKlasse, dass gun ( Sniper, ShotGun) die shoot()Funktion implementiert . Also, wenn wir die Waffe wechseln und schießen.

shooter.switchGun();
shooter.fire();

Wir haben das Verhalten der fire()Funktion geändert .


1

Um das zu erweitern, was Larsenal gesagt hat. Eine Schnittstelle ist ein Vertrag, dem alle implementierenden Klassen folgen müssen. Aus diesem Grund können Sie eine Technik verwenden, die als Programmierung für den Vertrag bezeichnet wird. Dadurch kann Ihre Software unabhängig von der Implementierung werden.


1

Schnittstellen werden im Allgemeinen verwendet, wenn Sie ein Verhalten definieren möchten, das Objekte aufweisen können.

Ein gutes Beispiel hierfür in der .NET-Welt ist die IDisposable- Schnittstelle, die in allen Microsoft-Klassen verwendet wird, die Systemressourcen verwenden, die manuell freigegeben werden müssen. Es erfordert, dass die Klasse, die es implementiert, über eine Dispose () -Methode verfügt.

(Die Dispose () -Methode wird auch vom using-Sprachkonstrukt für VB.NET und C # aufgerufen , das nur mit IDisposables funktioniert. )

Beachten Sie, dass Sie mithilfe von Konstrukten wie TypeOf ... Is(VB.NET), is(C #), instanceof(Java) usw. überprüfen können, ob ein Objekt eine bestimmte Schnittstelle implementiert.


1

Wie wahrscheinlich bereits mehrere Personen geantwortet haben, können Schnittstellen verwendet werden, um bestimmte Verhaltensweisen zwischen Klassen zu erzwingen, die diese Verhaltensweisen nicht auf die gleiche Weise implementieren. Wenn Sie also eine Schnittstelle implementieren, sagen Sie, dass Ihre Klasse das Verhalten der Schnittstelle hat. Die IAnimal-Schnittstelle wäre keine typische Schnittstelle, da Klassen für Hunde, Katzen, Vögel usw. Tierarten sind und sie wahrscheinlich erweitern sollten, was ein Fall von Vererbung ist. Stattdessen ähnelt eine Schnittstelle in diesem Fall eher dem Verhalten von Tieren, z. B. IRunnable, IFlyable, ITrainable usw.

Schnittstellen sind für viele Dinge gut, eines der wichtigsten Dinge ist die Steckbarkeit. Wenn Sie beispielsweise eine Methode mit einem List-Parameter deklarieren, kann alles, was die List-Schnittstelle implementiert, übergeben werden, sodass der Entwickler zu einem späteren Zeitpunkt eine andere Liste entfernen und einfügen kann, ohne eine Menge Code neu schreiben zu müssen.

Es ist möglich, dass Sie niemals Schnittstellen verwenden, aber wenn Sie ein Projekt von Grund auf neu entwerfen, insbesondere ein Framework, möchten Sie sich wahrscheinlich mit diesen vertraut machen.

Ich würde empfehlen, das Kapitel über Schnittstellen in Java Design von Coad, Mayfield und Kern zu lesen . Sie erklären es etwas besser als der durchschnittliche Einführungstext. Wenn Sie kein Java verwenden, können Sie einfach den Anfang des Kapitels lesen, bei dem es sich hauptsächlich um Konzepte handelt.


1

Wie alle Programmiertechniken, die Ihrem System Flexibilität verleihen, erhöhen auch die Schnittstellen die Komplexität. Sie sind oft großartig und können überall verwendet werden (Sie können eine Schnittstelle für alle Ihre Klassen erstellen). Auf diese Weise würden Sie jedoch ein komplexeres System erstellen, das schwieriger zu warten wäre.

Hier gibt es wie üblich einen Kompromiss: Flexibilität über Wartbarkeit. Welches ist wichtiger? Es gibt keine Antworten - es kommt auf das Projekt an. Aber denken Sie daran, dass jede Software gewartet werden muss ...

Also mein Rat: Verwenden Sie keine Schnittstellen, bis Sie sie wirklich brauchen. (Mit Visual Studio können Sie in 2 Sekunden eine Schnittstelle aus einer vorhandenen Klasse extrahieren - beeilen Sie sich also nicht.)

Wann müssen Sie jedoch eine Schnittstelle erstellen?

Ich mache es, wenn ich eine Methode umgestalte, die plötzlich zwei oder mehr ähnliche Klassen verarbeiten muss. Ich erstelle dann eine Schnittstelle, ordne diese Schnittstelle den zwei (oder mehr) ähnlichen Klassen zu und ändere den Methodenparametertyp (ersetze den Klassentyp durch den Schnittstellentyp).

Und es funktioniert: o)

Eine Ausnahme: Wenn ich Objekte verspotten soll, ist die Benutzeroberfläche viel einfacher zu bedienen. Deshalb erstelle ich oft nur dafür eine Schnittstelle.

PS: Wenn ich "Schnittstelle" schreibe, meine ich: "Schnittstelle einer beliebigen Basisklasse", einschließlich reiner Schnittstellenklassen. Beachten Sie, dass abstrakte Klassen oft eine bessere Wahl sind als reine Schnittstellen, da Sie ihnen Logik hinzufügen können.

Grüße, Sylvain.


1

Schnittstellen werden deutlich, wenn Sie Bibliotheksentwickler werden (jemand, der für andere Codierer codiert). Die meisten von uns beginnen als Anwendungsentwickler , wo wir vorhandene APIs und Programmierbibliotheken verwenden.

In dem Sinne, dass Schnittstellen ein Vertrag sind , hat noch niemand erwähnt, dass Schnittstellen eine großartige Möglichkeit sind, einige Teile Ihres Codes stabil zu machen . Das ist besonders nützlich , wenn es ein Team - Projekt (oder , wenn Sie Code entwickeln von anderen Entwicklern verwendet wird ). Hier ist ein konkretes Szenario für Sie:

Wenn Sie Code in einem Team entwickeln , verwenden möglicherweise andere den von Ihnen geschriebenen Code. Sie sind am glücklichsten, wenn sie auf Ihre (stabilen) Schnittstellen codieren, und Sie sind glücklich, wenn Sie die Freiheit haben, Ihre Implementierungen (versteckt hinter der Schnittstelle) zu ändern, ohne den Code Ihres Teams zu beschädigen. Es ist eine Variante des Versteckens von Informationen (Schnittstellen sind öffentlich, Implementierungen sind vor den Client-Programmierern verborgen). Lesen Sie mehr über geschützte Variationen .

Siehe auch diese verwandte Frage zum Codieren in eine Schnittstelle .


1

Es gibt so viele Zwecke für die Verwendung einer Schnittstelle.

  1. Verwendung im polymorphen Verhalten. Wo Sie bestimmte Methoden einer untergeordneten Klasse mit einer Schnittstelle aufrufen möchten, die auf die untergeordnete Klasse verweist.

  2. Einen Vertrag mit Klassen haben, um alle Methoden zu implementieren, wo dies erforderlich ist, wie es am häufigsten bei COM-Objekten der Fall ist, bei denen eine Wrapper-Klasse in einer DLL generiert wird, die die Schnittstelle erbt. Diese Methoden werden hinter den Kulissen aufgerufen, und Sie müssen sie nur implementieren, jedoch mit derselben Struktur wie in der COM-DLL definiert, die Sie nur über die Schnittstelle kennen, die sie verfügbar machen.

  3. Reduzieren der Speichernutzung durch Laden bestimmter Methoden in eine Klasse. Wenn Sie beispielsweise drei Geschäftsobjekte haben und diese in einer einzelnen Klasse implementiert sind, können Sie drei Schnittstellen verwenden.

ZB IUser, IOrder, IOrderItem

public interface IUser()
{

void AddUser(string name ,string fname);

}

// Same for IOrder and IOrderItem
//


public class  BusinessLayer: IUser, IOrder, IOrderItem

{    
    public void AddUser(string name ,string fname)
    {
        // Do stuffs here.
    }

    // All methods from all interfaces must be implemented.

}

Wenn Sie nur einen Benutzer hinzufügen möchten, gehen Sie folgendermaßen vor:

IUser user = new (IUser)BusinessLayer();

// It will load  all methods into memory which are declared in the IUser interface.

user.AddUser();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.