Sollte ich Vererbung verwenden?

8

Dies ist eher eine subjektive Frage, also werde ich sie präventiv als Community-Wiki markieren.

Grundsätzlich habe ich herausgefunden, dass es in den meisten Bereichen meines Codes viele Klassen gibt, von denen sich viele gegenseitig verwenden, von denen jedoch nur wenige direkt miteinander verwandt sind. Ich schaue zurück auf meine College-Tage und denke an die traditionellen class Cat : Animal type-Beispiele, wo Sie riesige Vererbungsbäume haben, aber ich sehe nichts davon in meinem Code. Meine Klassendiagramme sehen aus wie riesige Spinnennetze, nicht wie schöne Bäume.

Ich habe das Gefühl, dass ich die Informationen logisch gut trennen konnte, und in letzter Zeit habe ich gute Arbeit geleistet, Abhängigkeiten zwischen Klassen über DI / IoC-Techniken zu isolieren, aber ich mache mir Sorgen, dass mir etwas fehlen könnte. Ich tendiere dazu, Verhalten in Schnittstellen zu verklumpen, aber ich unterklassiere einfach nicht.

Ich kann Unterklassen anhand traditioneller Beispiele wie class Dog : Animal oder class Employee : Person leicht verstehen, aber ich habe einfach nichts so Offensichtliches, mit dem ich es zu tun habe. Und die Dinge sind selten so klar wie class Label : Control . Aber wenn es darum geht, echte Entitäten in meinem Code als Hierarchie zu modellieren, habe ich keine Ahnung, wo ich anfangen soll.

Also, ich denke, meine Fragen laufen darauf hinaus:

  1. Ist es in Ordnung, einfach nicht Unterklasse oder Vererben? Sollte ich überhaupt besorgt sein?
  2. In welchen Strategien müssen Sie Objekte ermitteln, die von der Vererbung profitieren könnten?
  3. Ist es akzeptabel, immer basierend auf dem Verhalten (Interfaces) zu erben anstatt auf dem tatsächlichen Typ?
drharris 22.07.2010, 18:12
quelle

11 Antworten

10

Die Vererbung sollte immer eine "ist-a" -Beziehung darstellen. Sie sollten in der Lage sein zu sagen "A ist ein B", wenn A von B abgeleitet ist. Wenn nicht, bevorzugen Sie die Komposition. Es ist vollkommen in Ordnung, keine Unterklasse zu bilden, wenn es nicht notwendig ist.

Wenn Sie beispielsweise sagen, dass FileOpenDialog "ist-a" Window sinnvoll ist, aber sagen, dass ein Engine "ist-a" Car ist Unsinn. In diesem Fall ist eine Instanz von Engine innerhalb einer Car -Instanz besser geeignet (Es kann gesagt werden, dass Car "in-terms-of-of-%" implementiert ist).

Für eine gute Erörterung der Vererbung siehe Teil 1 und Teil 2 von" Verwendungen und Missbrauch von Vererbung "auf gotw.ca.

    
In silico 22.07.2010, 18:29
quelle
3
  1. Solange Sie nicht den klaren Schnitt 'ist ein' Beziehungen, es ist in Ordnung und in der Tat ist, ist es am besten, nicht zu erben, sondern Komposition zu verwenden.

  2. is-a ist der Lackmustest. if (Ist X ein Y?) dann class X : Y { } else class X { Y myY; } oder class Y { X myX; }

  3. Die Verwendung von Schnittstellen, dh das Vererben von Verhalten, ist eine sehr gute Möglichkeit, den Code zu strukturieren, indem nur das erforderliche Verhalten und kein anderes hinzugefügt wird. Der schwierige Teil besteht darin, diese Schnittstellen gut zu definieren.

Vinko Vrsalovic 22.07.2010 18:21
quelle
3

Keine Technologie oder kein Muster sollte um seiner selbst willen verwendet werden. Sie arbeiten offensichtlich in einer Domäne, in der Klassen nicht von Vererbung profitieren, also sollten Sie keine Vererbung verwenden.

Sie haben DI verwendet, um die Dinge ordentlich und sauber zu halten. Sie haben die Sorgen Ihrer Klassen getrennt. Das sind alles gute Dinge. Versuchen Sie nicht, die Vererbung zu erzwingen, wenn Sie sie nicht wirklich brauchen.

Ein interessantes Follow-up zu dieser Frage wäre: Welche Programmierdomänen tendieren dazu, die Vererbung zu nutzen? (UI und Db-Frameworks wurden bereits erwähnt und sind gute Beispiele. Irgendwelche anderen?)

    
Paul Sasik 22.07.2010 18:31
quelle
2

Ich hasse auch den Hund - & gt; Säugetier - & gt; Tierische Beispiele, gerade weil sie im wirklichen Leben nicht vorkommen.

Ich verwende sehr wenig Unterklassen, weil es die Unterklasse eng an die Oberklasse koppelt und Ihren Code wirklich schwer lesbar macht. Manchmal ist die Implementationsvererbung nützlich (z. B. PostgreSQLDatabaseImpl und MySQLDatabaseImpl erweitern die AbstractSQLDatabase), aber die meiste Zeit macht es nur ein Durcheinander. Die meiste Zeit sehe ich Unterklassen das Konzept wurde missbraucht und entweder Schnittstellen oder eine Eigenschaft sollte verwendet werden.

Schnittstellen sind jedoch großartig und Sie sollten diese verwenden.

    
Sjoerd 22.07.2010 18:21
quelle
2

Im Allgemeinen bevorzugen Sie die Zusammensetzung über die Vererbung. Die Vererbung führt zu einer Verkapselung . z.B. Wenn eine Klasse von einer Methode einer Superklasse abhängt und die Superklasse die Implementierung dieser Methode in einigen Releases ändert, kann die Subklasse brechen.

Wenn Sie ein Framework entwerfen, haben Sie , um zu vererbende Klassen zu entwerfen. Wenn Sie die Vererbung verwenden möchten, müssen Sie dies sorgfältig dokumentieren und entwerfen. z.B. Kein Aufruf von Instanzmethoden (die von Ihren Unterklassen überschrieben werden könnten) im Konstruktor. Auch wenn es eine echte "ist-a" -Beziehung ist, ist Vererbung nützlich, ist aber robuster, wenn sie in einem Paket verwendet wird.

Siehe Effektives Java (Artikel 14 und 15). Es gibt ein gutes Argument dafür, warum Sie die Komposition gegenüber der Vererbung bevorzugen sollten. Es geht um Vererbung und Verkapselung im Allgemeinen (mit Java-Beispielen). Es ist also eine gute Ressource, auch wenn Sie nicht Java verwenden.

Also, um Ihre 3 Fragen zu beantworten:

Ist es in Ordnung, einfach keine Unterklasse zu erstellen oder zu erben? Sollte ich überhaupt besorgt sein? Antwort: Stellen Sie sich die Frage, ob es wirklich eine "Ist-Eine" Beziehung ist? Ist Dekoration möglich? Geh zur Dekoration

%Vor%

Welche Strategien müssen Sie verwenden, um Objekte zu ermitteln, die von der Vererbung profitieren könnten? Antwort: Wenn Sie ein Framework schreiben, möchten Sie normalerweise bestimmte Schnittstellen und "Basis" -Klassen bereitstellen, die speziell für die Vererbung entwickelt wurden.

Ist es akzeptabel, immer basierend auf dem Verhalten (Schnittstellen) zu erben anstatt auf dem tatsächlichen Typ? Antwort: Meistens ja, aber Sie wären besser dran, wenn die Superklasse für die Vererbung und / oder unter Ihrer Kontrolle ist. Oder gehen Sie für die Komposition.

    
naikus 22.07.2010 19:08
quelle
1

Meiner Meinung nach sollten Sie niemals # 3 machen, es sei denn, Sie erstellen eine abstrakte Basisklasse speziell für diesen Zweck, und ihr Name macht deutlich, wozu sie dient:

%Vor%

usw.

Auch bei dieser Art von Modell ist es manchmal hilfreich, wenn Sie eine Schnittstelle bereitstellen ( IDataProvider ), auch eine Basisklasse ( DataProviderBase ) bereitzustellen, die zukünftige Benutzer verwenden können, um bequem auf Logik zuzugreifen, die allen DataProvidern gemeinsam ist in Ihrem Anwendungsmodell.

Als allgemeine Regel verwende ich jedoch nur Vererbung, wenn ich eine echte "ist-a" -Beziehung habe, oder wenn es das Gesamtdesign für mich verbessert, create an ein "is-a" zu erstellen "Beziehung (z. B. Anbietermodell).

    
Toby 22.07.2010 18:24
quelle
1

Wo Sie Funktionen freigegeben haben, ist die Programmierung auf der Schnittstelle wichtiger als die Vererbung.

Vererbung bedeutet im Wesentlichen mehr, Objekte miteinander zu verknüpfen.

Die meiste Zeit beschäftigen wir uns damit, was ein Objekt tun kann, im Gegensatz zu dem, was es ist.

%Vor%

Sind die NewsItem und Article beide Content Elemente? Vielleicht, und Sie können es nützlich finden, in der Lage zu sein, eine Liste von Inhalt zu haben, der sowohl Article Artikel als auch NewsItem Artikel enthält.

Es ist jedoch wahrscheinlicher, dass Sie ähnliche Schnittstellen implementieren. Zum Beispiel könnte IRssFeedable eine Schnittstelle sein, die beide implementieren. Tatsächlich könnte Product diese Schnittstelle auch implementieren.

Dann können sie alle leicht in einen RSS-Feed geworfen werden, um Listen von Dingen auf Ihrer Webseite bereitzustellen. Dies ist ein großartiges Beispiel, wenn interface wichtig ist, während das Vererbungsmodell vielleicht weniger nützlich ist.

Bei der Vererbung geht es darum, die Art von Objekten zu identifizieren Bei Schnittstellen dreht sich alles darum, was Objekte tun können.

    
Atømix 22.07.2010 18:32
quelle
1

Meine Klassenhierarchien neigen dazu, auch ziemlich flach zu sein, mit Schnittstellen und Komposition, die die notwendige Kopplung bereitstellen. Die Vererbung scheint hauptsächlich dann aufzutauchen, wenn ich Sammlungen von Dingen speichere, wo die verschiedenen Arten von Dingen Daten / Eigenschaften gemeinsam haben. Vererbung fühlt sich für mich oft natürlicher an, wenn es gemeinsame Daten gibt, während Interfaces eine sehr natürliche Möglichkeit sind, gewöhnliches Verhalten auszudrücken.

    
Dan Bryant 22.07.2010 19:13
quelle
0

Die Antwort auf jede Ihrer drei Fragen lautet: "Es kommt darauf an". Letztendlich hängt alles von Ihrer Domain ab und was Ihr Programm damit macht. Oft finde ich, dass die Designmuster, die ich verwende, tatsächlich dabei helfen, Punkte zu finden, an denen die Vererbung gut funktioniert.

Betrachten Sie zum Beispiel einen "Transformator", der verwendet wird, um Daten in ein gewünschtes Formular zu mahlen. Wenn Sie drei Datenquellen als CSV-Dateien erhalten und diese in drei verschiedene Objektmodelle einfügen (und diese möglicherweise in einer Datenbank beibehalten), können Sie eine "csv-transformator" -Basis erstellen und dann einige Methoden überschreiben, wenn Sie von ihr erben um die verschiedenen spezifischen Objekte zu behandeln.

Das "Umwandeln" des Entwicklungsprozesses in die Mustersprache wird Ihnen helfen, Objekte / Methoden zu finden, die sich ähnlich verhalten und redundanten Code reduzieren (möglicherweise durch Vererbung, vielleicht durch die Verwendung von gemeinsam genutzten Bibliotheken - je nachdem, was am besten passt) / p>

Auch wenn Sie Ihre Ebenen getrennt halten (Geschäft, Daten, Präsentation usw.), wird Ihr Klassendiagramm einfacher, und Sie können dann die Objekte "visualisieren", die eigentlich vererbt werden sollen.

    
Assaf 22.07.2010 18:27
quelle
0

Ich würde mir keine Sorgen darüber machen, wie Ihr Klassendiagramm aussieht, Dinge sind selten wie im Klassenzimmer ...

Stellen Sie sich lieber zwei Fragen:

  1. Funktioniert Ihr Code?

  2. Ist die Pflege extrem zeitaufwendig? Erfordert eine Änderung manchmal die Änderung des gleichen Codes an vielen Stellen?

Wenn die Antwort auf (2) ja ist, möchten Sie vielleicht sehen, wie Sie Ihren Code strukturiert haben, um zu sehen, ob es eine vernünftigere Methode gibt, aber immer im Hinterkopf, dass Sie am Ende des Tages brauchen mit ja antworten können (1) ... Hübscher Code, der nicht funktioniert, nützt niemandem und ist dem Management nur schwer zu erklären.

    
Paddy 22.07.2010 19:44
quelle
0

IMHO, der Hauptgrund für die Verwendung der Vererbung besteht darin, dass Code, der für ein Basisklassenobjekt geschrieben wurde, stattdessen auf ein Objekt der abgeleiteten Klasse angewendet wird.

    
supercat 22.07.2010 21:02
quelle