Wenn Sie die kanonische, lehrbuchmäßige Antwort wollen, die die Leute seit dem Aufkommen von OOP geben (die Sie in diesen Antworten von vielen Leuten sehen), dann wenden Sie die folgende Regel an: "Wenn Sie eine is-a-Beziehung haben, verwenden Sie Vererbung. Wenn Sie eine has-a-Beziehung haben, verwenden Sie Komposition".
Das ist der übliche Ratschlag, und wenn Sie damit zufrieden sind, können Sie hier aufhören zu lesen und Ihren Weg fortsetzen. Für alle anderen...
is-a/has-a-Vergleiche haben Probleme
Zum Beispiel:
- Ein Quadrat ist ein Rechteck, aber wenn Ihre Rechteckklasse eine
setWidth()
/ setHeight()
Methoden, dann gibt es keinen vernünftigen Weg, um eine Square
erben von Rectangle
ohne Bruch Das Liskovsche Substitutionsprinzip .
- Eine ist-a-Beziehung kann oft so umformuliert werden, dass sie wie eine hat-a-Beziehung klingt. Zum Beispiel ist ein Angestellter eine Person, aber eine Person hat auch den Beschäftigungsstatus "angestellt".
- is-a-Beziehungen können zu unschönen Mehrfachvererbungshierarchien führen, wenn man nicht aufpasst. Schließlich gibt es im Englischen keine Regel, die besagt, dass ein Objekt est genau eine Sache.
- Die Leute sind schnell dabei, diese "Regel" weiterzugeben, aber hat jemals jemand versucht, sie zu untermauern oder zu erklären, warum es eine gute Heuristik ist, ihr zu folgen? Sicher, es passt gut zu der Idee, dass OOP die reale Welt modellieren soll, aber das ist an und für sich noch kein Grund, ein Prinzip zu übernehmen.
Voir diese StackOverflow-Frage für weitere Informationen zu diesem Thema.
Um zu wissen, wann man Vererbung und wann man Komposition verwenden sollte, muss man zunächst die Vor- und Nachteile der beiden Methoden kennen.
Die Probleme bei der Vererbung der Implementierung
Andere Antworten haben die Probleme mit der Vererbung sehr gut erklärt, so dass ich versuchen werde, hier nicht zu sehr ins Detail zu gehen. Aber hier ist eine kurze Liste:
- Bei der Vererbung wird wohl ein gewisses Maß an Implementierungsdetails an Unterklassen weitergegeben (über geschützte Mitglieder).
- Es kann schwierig sein, der Logik zu folgen, die sich zwischen den Methoden der Basis- und der Unterklassen bewegt.
- El Problem der fragilen Basis . Erschwerend kommt hinzu, dass die meisten OOP-Sprachen standardmäßig Vererbung zulassen - API-Designer, die nicht proaktiv verhindern, dass andere Personen von ihren öffentlichen Klassen erben, müssen besonders vorsichtig sein, wenn sie ihre Basisklassen überarbeiten. Leider wird das Problem der fragilen Basisklassen oft missverstanden, was dazu führt, dass viele nicht verstehen, was es braucht, um eine Klasse zu pflegen, von der jeder erben kann. (Ein Beispiel: Man kann eine Methode nicht so umgestalten, dass sie eine andere öffentliche Methode aufruft, selbst wenn sich das Verhalten der Basisklasse dadurch nicht ändert, weil eine Unterklasse die öffentliche Methode überschrieben haben könnte)
- El Tödlicher Diamant des Todes
Die Probleme mit der Zusammensetzung
- Es kann manchmal ein wenig langatmig sein.
Das war's. Ich meine es ernst. Das ist immer noch ein echtes Problem, aber im Allgemeinen ist es nicht so schlimm, zumindest im Vergleich zu den unzähligen Fallstricken, die mit der Vererbung verbunden sind.
Wann sollte die Vererbung eingesetzt werden?
Wenn Sie das nächste Mal Ihre schicken UML-Diagramme für ein Projekt entwerfen (falls Sie das tun) und darüber nachdenken, etwas Vererbung einzubauen, beachten Sie bitte folgenden Rat: Lassen Sie es.
Zumindest noch nicht.
Vererbung wird als Werkzeug zur Erreichung von Polymorphismus verkauft, aber damit verbunden ist ein mächtiges System zur Wiederverwendung von Code, das der meiste Code offen gesagt nicht braucht. Das Problem ist, dass man, sobald man seine Vererbungshierarchie öffentlich macht, auf diese bestimmte Art der Wiederverwendung von Code festgelegt ist, selbst wenn es für die Lösung des eigenen Problems zu viel ist.
Um dies zu vermeiden, würde ich sagen, dass Sie Ihre Basisklassen niemals öffentlich zugänglich machen sollten.
- Wenn Sie Polymorphismus benötigen, verwenden Sie eine Schnittstelle.
- Wenn Sie den Benutzern die Möglichkeit geben müssen, das Verhalten Ihrer Klasse anzupassen, sollten Sie explizite Einsprungpunkte über das Strategiemuster Außerdem ist es einfacher, diese Art von API stabil zu halten, da Sie die volle Kontrolle darüber haben, welche Verhaltensweisen sie ändern können und welche nicht.
- Wenn Sie versuchen, das Prinzip "offen-geschlossen" zu befolgen, indem Sie die Vererbung nutzen, um eine dringend benötigte Aktualisierung einer Klasse zu vermeiden, lassen Sie es einfach. Aktualisieren Sie die Klasse. Ihre Codebasis wird viel sauberer sein, wenn Sie sich tatsächlich um den Code kümmern, für den Sie angestellt sind, anstatt zu versuchen, etwas an die Seite zu tackern. Wenn Sie Angst haben, Fehler einzuführen, dann testen Sie den bestehenden Code.
- Wenn Sie Code wiederverwenden müssen, sollten Sie zunächst versuchen, Kompositions- oder Hilfsfunktionen zu verwenden.
Wenn Sie beschlossen haben, dass es keine andere gute Option gibt und Sie Vererbung verwenden müssen, um die benötigte Wiederverwendung von Code zu erreichen, dann können Sie sie verwenden, aber beachten Sie die folgenden vier Punkte P.A.I.L. Regeln für die eingeschränkte Vererbung, um sie vernünftig zu halten.
- Verwendung der Vererbung als privat Umsetzung im Detail. Legen Sie Ihre Basisklasse nicht öffentlich aus, sondern verwenden Sie dafür Schnittstellen. Auf diese Weise können Sie die Vererbung nach Belieben hinzufügen oder entfernen, ohne dass Sie eine einschneidende Änderung vornehmen müssen.
- Behalten Sie Ihre Basisklasse abstrakt . Es macht es einfacher, die Logik, die geteilt werden muss, von der Logik, die nicht geteilt werden muss, zu trennen.
- Isolieren Sie Ihre Basis- und Unterklassen. Lassen Sie nicht zu, dass Ihre Unterklasse Methoden der Basisklasse überschreibt (verwenden Sie dafür das Strategiemuster), und vermeiden Sie, dass sie Eigenschaften/Methoden voneinander erwarten, verwenden Sie andere Formen der gemeinsamen Nutzung von Code, um dies zu erreichen.
- Vererbung ist eine zuletzt Resort.
El Isolieren Sie Regel mag ein wenig hart klingen, aber wenn Sie sich disziplinieren, werden Sie einige schöne Vorteile haben. Insbesondere gibt sie Ihnen die Freiheit, alle oben erwähnten Fallstricke im Zusammenhang mit der Vererbung zu vermeiden.
- Sie geben keine Implementierungsdetails über geschützte Mitglieder preis. Sie haben keine Verwendung für protected, wenn Ihre Unterklassen bestrebt sind, die Eigenschaften, die die Basisklasse bietet, nicht zu kennen.
- Es ist viel einfacher, dem Code zu folgen, weil er sich nicht in und aus Basis- und Unterklassen verzweigt.
- Das Problem der brüchigen Basis verschwindet. Das Problem der zerbrechlichen Basisklasse rührt daher, dass man Methoden einer Basisklasse beliebig überschreiben kann, ohne dass man es merkt oder sich daran erinnert. Wenn die Vererbung isoliert und privat ist, ist die Basisklasse nicht anfälliger als eine Klasse, die durch Komposition von einer anderen abhängt.
- Der tödliche Diamant des Todes ist kein Thema mehr, da es einfach keine Notwendigkeit für mehrere Vererbungsschichten gibt. Wenn Sie die abstrakten Basisklassen B und C haben, die beide eine Menge Funktionalität teilen, verschieben Sie diese Funktionalität einfach aus B und C in eine neue abstrakte Basisklasse, Klasse D. Jeder, der von B geerbt hat, sollte aktualisieren, um sowohl von B als auch von D zu erben, und jeder, der von C geerbt hat, sollte von C und D erben. Da Ihre Basisklassen alle private Implementierungsdetails sind, sollte es nicht allzu schwierig sein, herauszufinden, wer von was erbt, um diese Änderungen vorzunehmen.
Schlussfolgerung
Mein erster Vorschlag wäre, in dieser Angelegenheit Ihren Verstand zu benutzen. Viel wichtiger als eine Liste von Regeln für die Verwendung von Vererbung ist ein intuitives Verständnis von Vererbung und den damit verbundenen Vor- und Nachteilen sowie ein gutes Verständnis der anderen Werkzeuge, die anstelle von Vererbung verwendet werden können (Komposition ist nicht die einzige Alternative. Zum Beispiel ist das Strategiemuster ein erstaunliches Werkzeug, das viel zu oft vergessen wird). Wenn Sie ein gutes, solides Verständnis all dieser Werkzeuge haben, werden Sie sich vielleicht dafür entscheiden, Vererbung häufiger zu verwenden, als ich es empfehlen würde, und das ist völlig in Ordnung. Zumindest treffen Sie eine fundierte Entscheidung und verwenden Vererbung nicht nur, weil Sie wissen, wie man es macht.
Lesen Sie weiter:
- Ein Artikel Ich habe zu diesem Thema einen Artikel geschrieben, der noch tiefer geht und Beispiele liefert.
- Eine Webpage über drei verschiedene Aufgaben, die Vererbung erfüllt, und wie diese Aufgaben mit anderen Mitteln in der Sprache Go erledigt werden können.
- Eine Auflistung Gründe, warum es sinnvoll sein kann, eine Klasse als nicht vererbbar zu deklarieren (z. B. "final" in Java).
6 Stimmen
Siehe auch welches Klassendesign besser ist
6 Stimmen
In einem Satz Vererbung ist öffentlich, wenn Sie eine öffentliche Methode haben und Sie ändern es ändert die veröffentlichte api. wenn Sie Zusammensetzung haben und das Objekt komponiert hat sich geändert, müssen Sie nicht Ihre veröffentlichte api ändern.