Ist es eine schlechte Angewohnheit, keine Schnittstellen zu benutzen? [geschlossen]

40

Ich benutze nur selten Schnittstellen und finde sie in anderen Codes häufig.

Außerdem erstelle ich in meinem Code selten Unter- und Superklassen (während ich meine eigenen Klassen erstelle).

  • Ist es eine schlechte Sache?
  • Würden Sie vorschlagen, diesen Stil zu ändern?
  • Hat dieser Stil irgendwelche Nebenwirkungen?
  • Liegt es daran, dass ich noch keine großen Projekte bearbeitet habe?
jineesh joseph
quelle
10
Welche Sprache ist das?
2
Abgesehen von welcher Sprache, welches Paradigma ist das?
Armando
1
Übersteigt das Konzept einer "Schnittstelle" nicht die Sprache? Java hat den integrierten Schnittstellentyp, aber C ++ - Entwickler erstellen häufig auch Schnittstellen (mit dem Präfix I), und alle dienen demselben Zweck.
Ricket
4
@Ricket: Nein, nicht wirklich. Das Problem ist, dass C ++ die Vererbung mehrerer Klassen bietet. In C ++ ist eine "Schnittstelle" viel konzeptioneller und in Wirklichkeit verwenden wir meistens das, was sie als abstrakte Basisklasse definieren würden. #
DeadMG
2
@Ricket nein, besonders wenn Sie in einer funktionalen Sprache und nicht in einer prozeduralen oder OOP-Sprache arbeiten.
Alternative

Antworten:

36

Es gibt mehrere Gründe, warum Sie Schnittstellen verwenden möchten:

  1. Schnittstellen eignen sich für Situationen, in denen Ihre Anwendungen viele möglicherweise nicht zusammenhängende Objekttypen benötigen, um bestimmte Funktionen bereitzustellen.
  2. Schnittstellen sind flexibler als Basisklassen, da Sie eine einzige Implementierung definieren können, die mehrere Schnittstellen implementieren kann.
  3. Schnittstellen sind in Situationen besser, in denen Sie die Implementierung nicht von einer Basisklasse erben müssen.
  4. Schnittstellen sind nützlich, wenn Sie die Klassenvererbung nicht verwenden können. Beispielsweise können Strukturen nicht von Klassen erben, aber sie können Schnittstellen implementieren.

http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx

Schnittstellen sind wie alles andere in der Programmierung. Wenn du sie nicht brauchst, benutze sie nicht. Ich habe gesehen, dass sie aus Gründen des Stils ausgiebig verwendet werden, aber wenn Sie nicht die speziellen Eigenschaften und Fähigkeiten benötigen, die eine Schnittstelle bietet, sehe ich nicht den Vorteil, sie "nur weil" zu verwenden.

Robert Harvey
quelle
13
Seine Sprache ist nicht angegeben, und Ihre Sprache ist zu spezifisch. In C ++ beispielsweise kann eine Struktur tatsächlich von einer Klasse erben, und Sie können nicht abstrakte Basen mehrfach erben.
DeadMG
2
Diese Antwort scheint C # zu sein, bezieht sich aber auch auf Java, wenn Sie # 4 herausnehmen, und gilt nur für C ++, da in C ++ natürlich Mehrfachvererbung zulässig ist.
Ricket
2
Auch mit der letzten Aussage bin ich nicht einverstanden. Ich denke, es gibt viele gute Praktiken, die Programmierer tun sollten, aber nicht, weil sie das Gefühl haben, sie brauchen sie nicht. Ich denke, der Fragesteller ist sehr weise darin, sich umzusehen, zu sehen, wie alle anderen dieses Ding tun, und zu denken, dass er es vielleicht auch tun sollte, aber zuerst nach dem "Warum" zu fragen.
Ricket
3
@Ricket: Das Warum steht in den vier Aufzählungspunkten meiner Antwort. Wenn keiner dieser Gründe zutrifft, benötigen Sie keine Schnittstelle.
Robert Harvey
17

Sowohl die Klassenvererbung als auch die Schnittstellen haben ihren Platz. Vererbung bedeutet "ist ein", während eine Schnittstelle einen Vertrag bereitstellt, der definiert, wie sich etwas "verhält".

Ich würde sagen, dass die häufigere Verwendung von Schnittstellen überhaupt keine schlechte Praxis ist. Ich lese gerade "Effective C # - 50 Specific Ways to Improve Your C #" von Bill Wagner. In Artikel 22 heißt es, und ich zitiere "Definieren und Implementieren von Schnittstellen der Vererbung vorziehen".

Im Allgemeinen verwende ich Basisklassen, wenn ich eine bestimmte Implementierung des gemeinsamen Verhaltens zwischen konzeptionell verwandten Typen definieren muss. Häufiger benutze ich Schnittstellen. Eigentlich beginne ich normalerweise damit, eine Schnittstelle für eine Klasse zu definieren, wenn ich damit beginne, eine zu erstellen. Auch wenn ich die Schnittstelle am Ende nicht kompiliere, finde ich, dass es hilfreich ist, zunächst die öffentliche API der zu definieren Klasse von Anfang an. Wenn ich feststelle, dass ich mehrere Klassen habe, die beide die Schnittstelle implementieren, und die Implementierungslogik identisch ist, werde ich mich nur dann fragen, ob es sinnvoll wäre, eine gemeinsame Basisklasse zwischen den Typen zu implementieren.

Ein paar Zitate aus Bill Wagners Buch ...

Alle abgeleiteten Klassen berücksichtigen dieses Verhalten sofort. Durch das Hinzufügen eines Members zu einer Schnittstelle werden alle Klassen getrennt, die diese Schnittstelle implementieren. Sie enthalten die neue Methode nicht und werden nicht mehr kompiliert. Jeder Implementierer muss diesen Typ aktualisieren, um das neue Mitglied aufzunehmen. Die Wahl zwischen einer abstrakten Basisklasse und einer Schnittstelle ist eine Frage, wie Sie Ihre Abstraktionen im Laufe der Zeit am besten unterstützen können. Schnittstellen sind fest: Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie enthalten die neue Methode nicht und werden nicht mehr kompiliert. Jeder Implementierer muss diesen Typ aktualisieren, um das neue Mitglied aufzunehmen. Die Wahl zwischen einer abstrakten Basisklasse und einer Schnittstelle ist eine Frage, wie Sie Ihre Abstraktionen im Laufe der Zeit am besten unterstützen können. Schnittstellen sind fest: Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie enthalten die neue Methode nicht und werden nicht mehr kompiliert. Jeder Implementierer muss diesen Typ aktualisieren, um das neue Mitglied aufzunehmen. Die Wahl zwischen einer abstrakten Basisklasse und einer Schnittstelle ist eine Frage, wie Sie Ihre Abstraktionen im Laufe der Zeit am besten unterstützen können. Schnittstellen sind fest: Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. " Sie geben eine Schnittstelle als Vertrag für eine Reihe von Funktionen frei, die jeder Typ implementieren kann. Basisklassen können im Laufe der Zeit erweitert werden. Diese Erweiterungen werden Teil jeder abgeleiteten Klasse. Die beiden Modelle können gemischt werden, um den Implementierungscode wiederzuverwenden und gleichzeitig mehrere Schnittstellen zu unterstützen. "

"Coding Interfaces bieten anderen Entwicklern eine größere Flexibilität als das Codieren in Basisklassentypen."

"Die Verwendung von Schnittstellen zum Definieren von APIs für eine Klasse bietet eine größere Flexibilität."

"Wenn Ihr Typ Eigenschaften als Klassentypen verfügbar macht, macht er die gesamte Schnittstelle für diese Klasse verfügbar. Mithilfe von Schnittstellen können Sie festlegen, dass nur die Methoden und Eigenschaften verfügbar gemacht werden, die Clients verwenden sollen."

"Basisklassen beschreiben und implementieren gemeinsames Verhalten über verwandte konkrete Typen hinweg. Schnittstellen beschreiben atomare Funktionalitätsteile, die nicht verwandte konkrete Typen implementieren können. Beide haben ihren Platz. Klassen definieren die Typen, die Sie erstellen. Schnittstellen beschreiben das Verhalten dieser Typen als Funktionalitätsteile. Wenn Sie die Unterschiede verstehen, werden Sie ausdrucksstärkere Designs erstellen, die angesichts von Änderungen widerstandsfähiger sind. Verwenden Sie Klassenhierarchien, um verwandte Typen zu definieren.

John Connelly
quelle
2
Nun, ich habe es gelesen und fand es eine gute Antwort.
Eric King
1
@ mc10 Bin mir nicht sicher, wo das Problem liegt. Er bezog sich auf ein Buch und die untere Hälfte seiner Antwort sind nur Zitate aus diesem Buch, die er Ihnen eindeutig mitteilt, falls Sie Ihre Zeit nicht verschwenden möchten.
Pete
@Pete Ich habe nicht gesagt, dass die Antwort schlecht ist, aber nur, dass es ein bisschen zu lang war. Ich bezweifle, dass Sie manchmal das gesamte Zitat brauchten.
Kevinji
3
haha, wenn das tl; dr ist, bin ich mir nicht sicher, ob dir diese ganze Sache mit StackExchange-Antworten von hoher Qualität gefallen wird.
Nic
haha danke jungs Ja, mir ist aufgefallen, dass die Antwort etwas langwierig ist, aber ich dachte, ich würde meine Antwort durch die Aufnahme relevanter Zitate von Herrn Wagner begrenzen, die die Vor- und Nachteile von Schnittstellen gegenüber Vererbung sowie die Szenarien erläutern, in denen jedes ist angemessener. Vielleicht hat das direkte Zitieren des Buches meiner Antwort nicht viel Wert verliehen.
John Connelly
8

Eine Sache , die nicht erwähnt wird getestet: in allen C # spöttisch-Bibliotheken, sowie einige für Java - Klassen können nicht verspottet werden , wenn sie eine Schnittstelle implementieren. Dies führt dazu, dass viele Projekte den Agile / TDD-Praktiken folgen und jeder Klasse eine eigene Schnittstelle zugewiesen wird.

Einige Leute halten diese bewährte Methode für "weniger Kopplung", aber ich bin anderer Meinung - ich denke, es ist nur eine Umgehung eines Sprachmangels.


Ich denke, dass Interfaces am besten verwendet werden, wenn Sie zwei oder mehr Klassen haben, die abstrakt gesehen dasselbe tun, aber auf unterschiedliche Weise.

Das .Net-Framework verfügt beispielsweise über mehrere Klassen, in denen Listen mit Inhalten gespeichert werden , die jedoch alle auf unterschiedliche Weise gespeichert werden. Es ist daher sinnvoll, eine abstrakte IList<T>Schnittstelle zu haben , die mit verschiedenen Methoden implementiert werden kann.

Sie sollten es auch verwenden, wenn 2+ Klassen austauschbar oder in Zukunft austauschbar sein sollen. Wenn in Zukunft eine neue Methode zum Speichern von Listen auf den Markt kommt, müssten AwesomeList<T>Sie, wenn Sie den IList<T>gesamten Code verwenden AwesomeList<T>, nur ein paar Dutzend Zeilen anstatt ein paar Hunderttausend Zeilen ändern.

BlueRaja - Danny Pflughoeft
quelle
5

Das Hauptergebnis, wenn Vererbung und Schnittstellen nicht verwendet werden, wenn sie angemessen sind, ist eine enge Kopplung . Es kann etwas schwierig sein, dies zu erkennen, aber normalerweise ist das offensichtlichste Symptom, wenn Sie eine Änderung vornehmen. Sie müssen häufig eine ganze Reihe anderer Dateien durchgehen, um Änderungen an einem Ripple-Effekt vorzunehmen.

Karl Bielefeldt
quelle
4

Nein, das ist überhaupt nicht schlecht. Explizite Schnittstellen sollten genau dann verwendet werden, wenn zwei Klassen mit einer gemeinsamen Schnittstelle zur Laufzeit austauschbar sein müssen. Wenn sie nicht austauschbar sein müssen, lassen Sie sie nicht erben. So einfach ist das. Vererbung ist zerbrechlich und sollte nach Möglichkeit vermieden werden. Wenn Sie dies vermeiden oder stattdessen Generika verwenden können, tun Sie dies.

Das Problem ist, dass in Sprachen wie C # und Java mit Generika zur schwachen Kompilierungszeit möglicherweise DRY verletzt wird, da es keine Möglichkeit gibt, eine Methode zu schreiben, die mit mehr als einer Klasse umgehen kann, es sei denn, alle Klassen erben von derselben Basis. C # 4 dynamic kann damit umgehen.

Die Sache ist, Vererbung ist wie globale Variablen - wenn Sie es hinzufügen und Ihr Code davon abhängt, hilft Gott Ihnen, es wegzunehmen. Sie können es jedoch jederzeit hinzufügen, und Sie können es sogar hinzufügen, ohne die Basisklasse zu ändern, indem Sie eine Art Wrapper verwenden.

DeadMG
quelle
1
Grund für die Ablehnung?
DeadMG
Ich bin mir nicht sicher, ob ich eine Gegenstimme habe. Das war eine gute Antwort.
Bryan Boettcher
3

Ja, die Verwendung von Schnittstellen ist wahrscheinlich nicht (oder zu selten) eine schlechte Sache. Interfaces (im abstrakten Sinne und das C # / Java-Sprachkonstrukt ist eine ziemlich gute Annäherung an diesen abstrakten Sinn) definieren explizite Interaktionspunkte zwischen Systemen und Subsystemen. Dies trägt zur Reduzierung der Kopplung bei und macht das System wartungsfreundlicher. Wie bei allem, was die Wartbarkeit verbessert, wird es umso wichtiger, je größer ein System ist.

Michael Borgwardt
quelle
3

Ich habe seit Jahren keine Schnittstelle mehr benutzt. Das liegt natürlich daran, dass ich seit Jahren fast ausschließlich in Erlang programmiere und das gesamte Konzept einer Schnittstelle einfach nicht existiert. (Das nächste, was du bekommst, ist ein "Verhalten" und das ist nicht wirklich dasselbe, es sei denn, du blinzelst wirklich hart und siehst sie aus dem Augenwinkel an.)

Ihre Frage ist also wirklich paradigmenabhängig (in diesem Fall OOP) und außerdem sehr sprachabhängig (es gibt OOP-Sprachen ohne Schnittstellen).

Nur meine richtige Meinung
quelle
2

Wenn Sie über die Verwendung von Java sprechen, liegt ein Grund für die Verwendung von Schnittstellen darin, dass sie die Verwendung von Proxy-Objekten ohne Codegenerierungsbibliotheken ermöglichen. Dies kann ein wesentlicher Vorteil sein, wenn Sie mit einem komplexen Framework wie Spring arbeiten. Darüber hinaus sind für einige Funktionen Schnittstellen erforderlich : RMI ist das klassische Beispiel dafür, da Sie die von Ihnen bereitgestellten Funktionen in Form von Schnittstellen (die von geerbt werden java.rmi.Remote) beschreiben müssen, unabhängig davon, wie Sie sie implementieren.

Donal Fellows
quelle
1

Die Dinge ändern sich ( MS Moles ), aber der Hauptgrund, warum ich denke, dass es gut ist, fast ausschließlich auf Schnittstellen zu programmieren, ist, dass sie leicht zu verspotten sind und sich auf natürliche Weise in eine IoC-Architektur einfügen.

IMO sollten Sie immer nur mit Schnittstellen arbeiten, oder, wo immer möglich, völlig dumme DAOs. Wenn Sie in diese Denkweise geraten und eine Bibliothek verwenden, die sich nicht über Schnittstellen ausstellt und alles über konkrete Objekte erledigt, muss ich ehrlich sein, dann fühlt sich alles ein bisschen klobig an.


quelle
0

Bei alten Modeprojekten werden Schnittstellen verwendet, um Zirkelverweise zu vermeiden, da Zirkelverweise auf lange Sicht ein großes Wartungsproblem darstellen können.

Schlecht:

class B; // forward declaration
class A
{
  B* b;
};

class B
{
  A* a;
}

Nicht schlecht:

class PartOfClassB_AccessedByA
{
};

class A
{
  PartOfClassB_AccessedByA* b;
};

class B : public PartOfClassB_AccessedByA
{
  A* a;
}

Normalerweise werden A, B, PartOfClassB_AccessedByA mit separaten Dateien implementiert.

9dan
quelle
0

Durch die schnittstellenbasierte Programmierung lässt sich Code flexibler und einfacher testen. Die Implementierungsklassen können geändert werden, ohne den Client-Code zu berühren [Flexibilität]. Beim Testen von Code kann man die eigentliche oInterface-basierte Programmierung ersetzen, um den Code flexibler und einfacher zu testen. Die Implementierungsklassen können geändert werden, ohne den Client-Code zu berühren [Flexibilität]. Beim Testen von Code kann man das tatsächliche Objekt durch Scheinobjekte [Testbarkeit] .bject durch Scheinobjekte [Testbarkeit] ersetzen.

Amit Patel
quelle