Frage

Dies ist eine etwas Follow-up-Frage dazu Frage.

Angenommen, ich habe einen Vererbungsbaum wie folgt:

Car -> Ford -> Mustang -> MustangGT

Gibt es einen Vorteil für die Definition von Schnittstellen für jeder dieser Klassen? Beispiel:

ICar -> IFord -> IMustang -> IMustangGT

Ich kann das vielleicht andere Klassen sehen (wie Chevy) würde implementieren wollen Icar oder IFord Und vielleicht sogar IMustang, aber wahrscheinlich nicht IMustangGT Weil es so spezifisch ist. Sind die Schnittstellen in diesem Fall überflüssig?

Ich würde auch denken, dass jede Klasse, die implementieren möchte IFord würde definitiv sein eine Erbe nutzen wollen, indem er von Erben von erben Ford um Code nicht zu duplizieren. Wenn das selbstverständlich ist, was ist der Vorteil der ebenfalls Umsetzung IFord?

War es hilfreich?

Lösung

Nach meiner Erfahrung werden Schnittstellen am besten verwendet, wenn Sie mehrere Klassen haben, die jeweils auf dieselbe Methode oder Methoden reagieren müssen, damit sie von einem anderen Code austauschbar verwendet werden können, der anhand der gemeinsamen Schnittstelle dieser Klassen geschrieben wird. Die beste Verwendung einer Schnittstelle ist, wenn das Protokoll wichtig ist, die zugrunde liegende Logik jedoch für jede Klasse unterschiedlich sein kann. Wenn Sie sonst die Logik duplizieren würden, sollten Sie stattdessen abstrakte Klassen oder Standardklassenvererbung in Betracht ziehen.

Und als Antwort auf den ersten Teil Ihrer Frage würde ich empfehlen, eine Schnittstelle für jeden Ihrer Klassen zu erstellen. Dies würde Ihre Klassenstruktur unnötig überladen. Wenn Sie feststellen, dass Sie eine Schnittstelle benötigen, können Sie sie immer später hinzufügen. Hoffe das hilft!

Adam

Andere Tipps

Ich stimme auch mit Adamalexs Antwort Diese Schnittstellen sollten von Klassen geteilt werden, die auf bestimmte Methoden reagieren sollten.

Wenn Klassen eine ähnliche Funktionalität aufweisen, jedoch in einer Stammbeziehung nicht direkt miteinander verbunden sind, wäre eine Schnittstelle eine gute Möglichkeit, diese Funktion den Klassen hinzuzufügen, ohne die Funktionalität zwischen beiden zu duplizieren. (Oder haben mehrere Implementierungen mit nur subtilen Unterschieden.)

Während wir eine Autoanalogie verwenden, ein konkretes Beispiel. Nehmen wir an, wir haben die folgenden Klassen:

Car -> Ford   -> Escape  -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid

Autos haben wheels und kann Drive() und Steer(). Diese Methoden sollten also in der existieren Car Klasse. (Wahrscheinlich die Car Klasse wird eine abstrakte Klasse sein.)

Wenn wir auf der ganzen Linie gehen, erhalten wir die Unterscheidung zwischen Ford und Toyota (Wahrscheinlich als Unterschied in der Art von Emblem im Auto implementiert, wahrscheinlich wieder als abstrakte Klasse.)

Dann haben wir endlich eine Escape und Corolla Klasse, die Klassen sind, die vollständig als Auto implementiert werden.

Wie könnten wir nun eine machen? Hybrid Fahrzeug?

Wir könnten eine Unterklasse von haben Escape das ist EscapeHybrid was a fügt a FordsHybridDrive() Methode und eine Unterklasse von Corolla das ist CorollaHybrid mit ToyotasHybridDrive() Methode. Die Methoden tun im Grunde dasselbe, aber wir haben unterschiedliche Methoden. Yuck. Es scheint, als ob wir es besser machen können.

Nehmen wir an, ein Hybrid hat eine HybridDrive() Methode. Da wir nicht zwei verschiedene Arten von Hybriden haben wollen (in einer perfekten Welt), können wir eine machen IHybrid Schnittstelle mit a HybridDrive() Methode.

So, Wenn wir eine machen wollen EscapeHybrid oder CorollaHybrid Klasse, Alles was wir tun müssen, ist Implementieren Sie die IHybrid Schnittstelle.

Schauen wir uns Java an, für ein Beispiel in der realen Welt. Eine Klasse, die einen Vergleich eines Objekts mit einem anderen Objekt durchführen kann, implementiert die Comparable Schnittstelle. Wie der Name schon sagt, sollte die Schnittstelle für eine Klasse sein, die ist vergleichbar, daher der Name "vergleichbar".

In Interesse, a CAR -Beispiel wird in der verwendet Schnittstellen Lektion der Java -Tutorial.

Sie sollten überhaupt keine dieser Schnittstellen implementieren.

Klassenvererbung beschreibt Was für ein Objekt ist (zB: Es ist Identität). Dies ist in Ordnung, aber die meiste Zeit was für ein Objekt ist, ist weit weniger wichtig als das, was für ein Objekt tut. Hier kommen Schnittstellen ins Spiel.

Eine Schnittstelle sollte beschreiben Was für ein Objekt tut), oder wie es sich handelt. Damit meine ich es ist Verhalten und die Operationen, die angesichts dieses Verhaltens sinnvoll sind.

Als solches sollten gute Schnittstellennamen normalerweise von der Form sein IDriveable, IHasWheels, usw. Manchmal ist der beste Weg, um dieses Verhalten zu beschreiben, auf ein bekanntes anderes Objekt zu verweisen, sodass Sie sagen können, dass "wirkt wie eines davon" (z. B.: IList) Aber ich bin diese Form der Benennung in der Minderheit.

Angesichts dieser Logik die Szenarien wo Schnittstellenvererbung Der Sinn macht Sinn völlig und völlig anders als die Szenarien, in denen Objektvererbung macht Sinn - oft beziehen sich diese Szenarien überhaupt nicht aufeinander.

Ich hoffe, das hilft Ihnen, die Schnittstellen durchzudenken, die Sie tatsächlich brauchen sollten :-)

Ich würde sagen, nur eine Schnittstelle für Dinge, auf die Sie sich beziehen müssen. Möglicherweise haben Sie einige andere Klassen oder Funktionen, die über ein Auto wissen müssen, aber wie oft muss es etwas über einen Ford wissen?

Bauen Sie keine Sachen auf, die Sie nicht brauchen. Wenn sich herausstellt, dass Sie die Schnittstellen benötigen, ist es eine kleine Anstrengung, sie zurückzukehren und zu bauen.

Außerdem hoffe ich auf der pedantischen Seite, dass Sie nicht wirklich etwas bauen, das wie diese Hierarchie aussieht. Dies ist nicht das, wofür die Vererbung verwendet werden sollte.

Erstellen Sie es nur einmal, wenn diese Funktionalitätsstufe notwendig wird.

Der Refaktorcode erfolgt immer im laufenden Prozess.

Es stehen Tools zur Verfügung, mit denen Sie bei Bedarf an Schnittstelle extrahieren können. Z.B http://geekswithblogs.net/jaysmith/archive/2008/02/27/refactor-visual-studio-extract-interface.aspx

Machen Sie einen ICAR und den ganzen Rest (make = ford, model = mustang und stuff) als Mitglieder einer Klasse, die die Schnittstelle implementiert.

Vielleicht möchten Sie Ihre Ford -Klasse und beispielsweise die GM -Klasse und beide ICAR implementieren, um den Polymorphismus zu verwenden, wenn Sie nicht den Weg der Überprüfung hinuntergehen möchten Make == Whatever, Das liegt in Ihrem Stil.

Wie auch immer - meiner Meinung nach sind dies Attribute eines Autos, das nicht umgekehrt ist - Sie benötigen nur eine Schnittstelle, da Methoden üblich sind: Bremse, Beschleunigung usw.

Kann ein Ford Sachen tun, die andere Autos nicht können? Ich glaube nicht.

Ich habe die ersten beiden Ebenen, ICAR und Iford, erstellen und lasse die zweite Ebene in Ruhe, bis ich eine Schnittstelle auf dieser zweiten Ebene benötige.

Überlegen Sie sorgfältig, wie Ihre Objekte innerhalb Ihrer Problemdomäne miteinander interagieren müssen, und überlegen Sie, ob Sie mehr als eine Implementierung eines bestimmten abstrakten Konzepts benötigen. Verwenden Sie Schnittstellen, um einen Vertrag um ein Konzept zu stellen, mit dem andere Objekte interagieren.

In Ihrem Beispiel würde ich vorschlagen, dass Ford wahrscheinlich ein Hersteller ist und Mustang ein Modellname -Wert ist, der vom Hersteller Ford verwendet wird. Daher haben Sie möglicherweise etwas mehr wie:

IVehichle -> CarImpl, MotorbikeImpl -Has -A -Hersteller hat -viele Modellnamen

In dieser Antwort über die Unterschied zwischen Schnittstelle und Klasse, Ich habe das erklärt:

  • Schnittstelle zeigt, was für ein Konzept ist (in der Laufzeit von "Was ist"gültig, zur Zusammenstellung des Zeitpunkts) und wird für verwendet Werte (MyInterface x = ...)
  • Klasse zeigt, was für ein Konzept tut (tatsächlich zur Laufzeit ausgeführt) und wird für Werte oder für Objekte verwendet (MyClass X oder Amyclass.Method ())

Wenn Sie also in eine "Ford" -Variable (Begriff "Wert") verschiedene Unterklassen von Ford speichern müssen, erstellen Sie einen Iford. Ansonsten stören Sie sich nicht, bis Sie es tatsächlich brauchen.

Das ist ein Kriterium: Wenn es nicht erfüllt ist, ist Iford wahrscheinlich nutzlos.
Wenn es erfüllt ist, gelten die anderen in den vorherigen Antworten aufgedeckten Kriterien: Wenn ein Ford eine reichhaltigere API als ein Auto hat, ist ein Iford für Polymorphismen nützlich. Wenn nicht, reicht ICAR aus.

Meiner Ansicht nach sind Schnittstellen ein Instrument zur Durchsetzung einer Anforderung, dass eine Klasse eine bestimmte Signatur implementiert oder (wie ich gerne daran denke) ein bestimmtes "Verhalten" für mich, denke ich, wenn das Kapital ich zu Beginn meiner Onterface -Namen als Ein Personalpronomen, und ich versuche, meine Schnittstellen zu benennen, damit sie so gelesen werden können ... iCanfly, iKnowhowtopersistmyself iamdisplayable usw. Also würde ich in Ihrem Beispiel keine Schnittstelle erstellen, um die vollständige öffentliche Signatur einer bestimmten Person widerzuspiegeln Klasse. Ich würde die öffentliche Signatur (das Verhalten) analysieren und dann die Mitglieder in kleinere logische Gruppen (desto kleiner desto besser) wie (mit Ihrem Beispiel) IMOVE, IUSEFUEL, ICARRYPASSERGERS, TESTEERBLICH, IACCELERATE, IDEPRECIATE usw. Diese Schnittstellen zu anderen Klassen in meinem System brauchen sie

Im Allgemeinen besteht der beste Weg, darüber nachzudenken (und viele Fragen in OO), über den Begriff eines Vertrags nachzudenken.

Ein Vertrag wird als Vereinbarung zwischen zwei (oder mehr) Parteien definiert, dass Staaten spezifische Verpflichtungen, die jede Partei erfüllen muss; In einem Programm wird dies die Dienste, die eine Klasse anbietet, und was Sie für die Klasse zur Verfügung stellen müssen, um die Dienste zu erhalten. Eine Schnittstelle gibt einen Vertrag an, den jede Klasse, die die Schnittstelle implementiert, erfüllen muss.

In diesem Sinne hängt Ihre Frage jedoch etwas davon ab, welche Sprache Sie verwenden und was Sie tun möchten.

Nach vielen Jahren des OO (wie, oh mein Gott, 30 Jahre), schrieb ich normalerweise eine Schnittstelle für jeden Vertrag, insbesondere in Java, weil sie Tests so viel einfacher macht: Wenn ich eine Schnittstelle für die Klasse habe, kann ich bauen Mockobjekte leicht, fast trivial.

Schnittstellen sollen eine generische öffentliche API sein, und Benutzer werden auf die Verwendung dieser öffentlichen API beschränkt. Es sei denn, Sie beabsichtigen, Benutzer die typspezifischen Methoden von zu verwenden IMustangGT, Möglicherweise möchten Sie die Schnittstellenhierarchie auf einschränken ICar und IExpensiveCar.

Nur von Schnittstellen und abstrakten Klassen erben.

Wenn Sie ein paar Klassen haben, die fast gleich sind und Sie die Mehrheit der Methoden, die Verwendung und die Schnittstelle in Kombination mit dem Kauf des anderen Objekts implementieren müssen.
Wenn die Mustang -Klassen so unterschiedlich sind, erstellen Sie nicht nur eine Schnittstelle ICAR, sondern auch Imustang.
Also können Klasse Ford und Mustang von ICAR und Mustang und Mustanggt von ICAR erben und Imustang.

Wenn Sie Klassen Ford implementieren und eine Methode mit Mustang identisch ist, kaufen Sie bei Mustang:

class Ford{
  public function Foo(){
    ...
    Mustang mustang  = new Mustang();
    return mustang.Foo();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top