Sollte sich jede Klasse, die ich schreibe, an eine Schnittstelle halten?

10

Ich schreibe ein Spiel in Typescript und habe beschlossen, mich an die Idee der " schnittstellenbasierten Programmierung " zu halten, bei der Sie Code auf der Grundlage einer Schnittstelle anstelle der Implementierung eines Objekts schreiben.

Ich habe eine gute Anzahl von Schnittstellen und Klassen geschrieben, die sie implementieren, bin dann einen Schritt zurückgetreten und habe festgestellt, dass die Klassen so einfach sind, dass ich die Implementierung wahrscheinlich nie ändern muss, da es wirklich nur einen Weg gibt, das zu tun, was die Klasse tut (bewegt sich Phaser.Spriteauf eine eingeschränkte Weise, um sich wie ein Panzer zu verhalten).

Dann erinnere ich mich, dass ich vor einigen Jahren über die Idee von YAGNI gelesen habe. Grundsätzlich sollten Sie Ihren Code nicht überentwickeln , um Dinge einzuschließen , die Sie möglicherweise nie verwenden.

Sollte jede Klasse nach den Best Practices eine Schnittstelle implementieren oder auf Klassen beschränken, von denen Sie erwarten, dass sie in Zukunft möglicherweise ausgetauscht werden?

Carcigenicate
quelle
Denken Sie daran, wenn Sie eine tatsächliche Klasse als Parameter verwenden, codieren Sie auch eine Schnittstelle, die Schnittstelle dieser Klasse. Niemand zwingt Sie, diese bestimmte Klasse zu verwenden. Sie könnten sie genauso gut erben und völlig neue Funktionen bereitstellen, aber das scheint nicht richtig zu sein. Es gibt Schnittstellen, die es den Menschen erleichtern, sich mit der Idee zu beschäftigen. Wenn das traurig ist, nein, Sie brauchen nicht wirklich Schnittstellen für alles.
Andy

Antworten:

6

Der Grund für Schnittstellen liegt darin, dass sie den Polymorphismus vereinfachen. Das heißt, Sie können Instanzen vertraglich senden, anstatt deren tatsächliche Implementierung zu kennen. Beispielsweise können Sie einen "Reader" an eine Methode senden, damit eine solche aufgerufene Methode die "read ()" - Methode verwenden kann. Indem Sie eine Schnittstelle als "Reader" deklarieren, können Sie jedes Objekt an diesen Vertrag anpassen, indem Sie die darin angegebenen Methoden implementieren. Auf diese Weise kann jeder Anrufer davon ausgehen, dass bestimmte Methoden existieren, auch wenn die dem Vertrag zugrunde liegenden Objekte möglicherweise völlig unterschiedlich sind.

Dies entkoppelt den Polymorphismus von einer Basisklasse und ermöglicht es auch zwischen Klassenkettengrenzen, indem ein Vertrag impliziert wird.

Jetzt ... Dies ist nur dann nützlich, wenn mehrere Vererbungsketten auf dieselbe Weise funktionieren müssen oder wenn Sie viele Basisklassen mit allgemeinem Verhalten haben, die Sie an andere Benutzer weitergeben möchten.

Wenn Sie nur EINE Vererbungskette (Basisklasse-> Unterklasse) oder nur die Basis haben oder die zugehörigen Objekte nicht an eine andere Person weitergeben oder generisch verwenden müssen, würden Sie keine Schnittstellenimplementierungen hinzufügen, wie dies dann der Fall ist nutzlos.

Richard Tyregrim
quelle
Ich möchte auch hinzufügen, dass Schnittstellen Abstraktionen modellieren sollten. Erstellen Sie sie also nicht, indem Sie einfach alle öffentlichen Mitglieder einer Klasse hochziehen. Überlegen Sie, was diese Klasse darstellt, und fügen Sie nur die Dinge in die Schnittstelle ein, die ein Objekt dieses Typs haben sollte. Möglicherweise erstellen Sie auch mehrere Schnittstellen (z. B. Movesund Shootsfür Ihr Tankbeispiel) für eine einzelne Klasse.
TMN
7

Wenn Sie nicht sicher sind, ob Sie die Schnittstellen tatsächlich benötigen, tun Sie dies wahrscheinlich nicht. Dies ist der Kern des YAGNI-Prinzips. Wenn Sie die Implementierung austauschen müssen, führen Sie eine Schnittstelle ein.

JacquesB
quelle
6

Das Erstellen einer Schnittstelle für jede Klasse ist etwas übertrieben. Aus rein objektorientierter Sicht hat jede Klasse bereits eine Schnittstelle . Eine Schnittstelle ist nichts anderes als die öffentlich zugänglichen Methoden und Datenelemente einer Klasse.

Unter "Schnittstelle" versteht man normalerweise "eine Java-Klasse, die mit dem interfaceSchlüsselwort definiert wurde " oder "eine C ++ - Klasse mit nur öffentlichen, rein virtuellen Funktionen". Dies sind nützliche Abstraktionen, da sie die Schnittstelle einer Klasse von ihrer Implementierung entkoppeln.

Verwenden Sie daher gegebenenfalls Schnittstellen.

  • Wird es mehrere Implementierungen für eine Schnittstelle geben? In diesem Fall kann diese Situation ein Kandidat für das Extrahieren gängiger, öffentlich zugänglicher Methoden in eine Schnittstelle sein.

  • Übergebe ich Implementierungen einer potenziellen Schnittstelle an andere Module, die das Innenleben meines Moduls nicht kennen sollten? Eine Schnittstelle kann hier nützlich sein, da sie die Größe des Berührungspunkts zwischen Modulen verringert.

Beachten Sie die obigen Wieselwörter: "kann" ein Kandidat sein, "kann" nützlich sein. Es gibt keine feste Regel, die für eine bestimmte Situation "Ja" oder "Nein" sagt.

Davon abgesehen ist es höchstwahrscheinlich übertrieben, eine Schnittstelle für alles zu haben. Wenn Sie dieses Muster sehen, gehen Sie zu weit:

interface I {
  int getA()
  int getB()
  ...
  int getY()
  int getZ()
}

class C : I {
  int getA() { ... }
  int getB() { ... }
  ...
  int getY() { ... }
  int getZ() { ... }
}

quelle
Ein weiterer Grund für Schnittstellen ist, dass Ihr Testsystem dies erforderlich macht, was von Ihrer Sprache und Ihrem Test-Framework abhängen kann. Hoffentlich müssen Sie das natürlich nicht tun.
Darien
@Darien: Der Grund, warum ein Testsystem möglicherweise Schnittstellen benötigt, liegt darin, dass der Test tatsächlich eine andere (verspottete) Implementierung verwendet, sodass Sie zum ersten Punkt zurückkehren, an dem eine Schnittstelle geeignet ist.
Bart van Ingen Schenau
Einige tolle Sachen hier. Denken Sie nur darüber nach: Eine Schnittstelle ist nichts anderes als die öffentlich zugänglichen Methoden und Datenelemente einer Klasse . Während dies für eine Schnittstelle wahr ist Implementierung , ist es sicherlich nicht wahr hält für eine Schnittstelle , die nie umgesetzt wird, obwohl gewährt - das ist etwas von einem Code Geruch wäre.
Robbie Dee
@RobbieDee es ist zB für ein Java wahr interface, es kommt einfach so vor, dass alles in Java interfaceauch seine öffentlich zugängliche Schnittstelle (OO-Konzept) ist.
Jeder! Schreiben Sie den 2. und 3. Satz der Antwort auf. Laminieren Sie es. Steck es in deine Brieftasche. Referenz oft.
Radarbob
5

Für neuere Entwickler kann es sehr verlockend sein, Schnittstellen als eine Unannehmlichkeit zu betrachten, die Sie einfach hinzufügen, nur weil Ihnen gesagt wird, dass dies "Best Practice" ist. Wenn Sie sind blind dies zu tun, schmatzt es von Cargo - Kult - Programmierung .

Es gibt eine Reihe sehr guter Gründe, warum Sie Schnittstellen für größere Entwicklungen in Betracht ziehen sollten, anstatt eine Reihe von Klassen zusammenzufassen.

Verspottende Frameworks

Wenn Sie eine mehrschichtige Anwendung testen möchten, ohne Objekte für die verschiedenen Ebenen erstellen zu müssen, sollten Sie zweifellos Mocking-Frameworks verwenden, um die Ebenen zu verspotten. Schnittstellen werden hier häufig verwendet.

Abhängigkeitsspritze

Während sie aufgrund des Aufblähens, das sie hinzufügen, etwas in Ungnade gefallen sind, ist die Idee solide - die Fähigkeit, Konkretionen basierend auf einer Schnittstelle einfach einzutauschen. Das Prinzip findet sich auch in vielen Entwurfsmustern wie dem Fabrikmuster.


Diese Ansätze sind im D von SOLID-Prinzipien zusammengefasst . Das Problem dabei ist, dass Sie Abstraktionen verwenden, keine Konkretionen.

Wie bei den Entwurfsmustern selbst ist es ein Urteilsspruch, wann sie verwendet werden sollen. Der entscheidende Punkt ist, zu verstehen, wie nützlich die Schnittstellen sind, und sie nicht nur in Ihre Klassen zu stecken, weil Sie das Gefühl haben, dass Sie es müssen. Es gibt eine gute Diskussion über die verschiedenen Vorzüge der DIP und YAGNI hier .

Robbie Dee
quelle
Beachten Sie, dass Abhängigkeitsinjektion und IoC-Container zwei völlig unterschiedliche Dinge sind (eines ist ein Entwurfsprinzip und das andere ist eine Möglichkeit, dieses Prinzip mithilfe verschiedener Frameworks konkret in die Praxis umzusetzen). Sie können DI ohne Verwendung eines IoC-Containers verwenden.
Sara
@kai "Sie können DI ohne Verwendung eines IoC-Containers verwenden" . Ich denke, reifere Entwicklungsgeschäfte können (und tun). Es besteht einfach keine Notwendigkeit, den Code für ein angeblich einfaches Konzept zu verschmutzen. Joel ist einer ähnlichen Ansicht.
Robbie Dee
YAGNI ist sehr knifflig. Was auch immer die Philosophie ist, in den meisten Fällen wird YAGNI in der Realität als Ausrede für Abstriche benutzt. Vermeiden Sie Kopplungen sollten ernst genommen werden. Es ist frustrierend zu sehen, dass sich ein Entwickler in vielen Scrum-Meetings als blockiert bezeichnet, weil jemand anderes seinen Job nicht beendet hat. Wie kommt es, dass das Blockieren eines anderen Entwicklers Zeit spart? Mein Vorschlag ist, eine Schnittstelle zu verwenden, um eine Kopplung zu vermeiden. Natürlich muss man keine Schnittstelle für Modellklassen verwenden.
Ripal Barot