Es ist sinnlos, darüber nachzudenken, ob Vererbung (oder wirklich ein einzelnes Merkmal) notwendig ist oder nicht, ohne auch den Rest der Semantik der Sprache zu berücksichtigen. Sie streiten in einem Vakuum.
Was Sie brauchen, ist eine konsequente Philosophie des Sprachdesigns. Die Sprache muss in der Lage sein, die Probleme, für die sie entwickelt wurde, elegant zu lösen. Das Modell, um dies zu erreichen, erfordert möglicherweise eine Vererbung oder nicht, aber es ist schwierig, dies ohne das Gesamtbild zu beurteilen.
Wenn Ihre Sprache beispielsweise über erstklassige Funktionen, Teilfunktionsanwendungen, polymorphe Datentypen, Typvariablen und generische Typen verfügt, haben Sie nahezu dieselben Grundlagen wie bei der klassischen OOP-Vererbung, jedoch unter Verwendung eines anderen Paradigmas.
Wenn Sie über eine späte Bindung, dynamische Typisierung, Methoden als Eigenschaften, flexible Funktionsargumente und erstklassige Funktionen verfügen, decken Sie dieselben Gründe ebenfalls ab, verwenden jedoch wieder ein anderes Paradigma.
(Das Finden von Beispielen für die beiden beschriebenen Paradigmen bleibt dem Leser als Übung.)
Denken Sie also über die Art der Semantik nach, die Sie möchten, spielen Sie mit ihnen herum und prüfen Sie, ob sie ohne Vererbung ausreichen. Wenn dies nicht der Fall ist, können Sie entweder entscheiden, die Vererbung in die Mischung zu werfen, oder Sie können entscheiden, dass etwas anderes fehlt.
Ja, es ist eine durchaus vernünftige Entwurfsentscheidung, die Vererbung wegzulassen.
Es gibt in der Tat sehr gute Gründe, die Implementierungsvererbung zu entfernen, da dies zu äußerst komplexem und schwer zu wartendem Code führen kann. Ich würde sogar so weit gehen, Vererbung (wie sie normalerweise in den meisten OOP-Sprachen implementiert ist) als Fehlfunktion zu betrachten.
Clojure bietet beispielsweise keine Implementierungsvererbung und bevorzugt eine Reihe von orthogonalen Merkmalen (Protokolle, Daten, Funktionen, Makros), mit denen dieselben Ergebnisse erzielt werden können, jedoch viel sauberer.
Hier ist ein Video, das ich zu diesem allgemeinen Thema sehr aufschlussreich fand. Rich Hickey identifiziert grundlegende Komplexitätsquellen in Programmiersprachen (einschließlich Vererbung) und präsentiert Alternativen für jede: Einfach gemacht leicht
quelle
Als ich zum ersten Mal auf die Tatsache stieß, dass VB6-Klassen keine Vererbung unterstützen (nur Schnittstellen), hat mich das wirklich geärgert (und tut es immer noch).
Der Grund dafür ist jedoch, dass es auch keine Konstruktorparameter gab, sodass Sie keine normale Abhängigkeitsinjektion (DI) durchführen konnten. Wenn Sie DI haben, ist dies wichtiger als die Vererbung, da Sie dem Prinzip folgen können, die Komposition der Vererbung vorzuziehen. Das ist sowieso eine bessere Möglichkeit, Code wiederzuverwenden.
Keine Mixins? Wenn Sie eine Schnittstelle implementieren und die gesamte Arbeit dieser Schnittstelle an ein Objekt delegieren möchten, das durch Abhängigkeitsinjektion festgelegt wurde, sind Mixins ideal. Andernfalls müssen Sie den gesamten Boilerplate-Code schreiben, um jede Methode und / oder Eigenschaft an das untergeordnete Objekt zu delegieren. Ich mache es viel (dank C #) und es ist eine Sache, die ich mir wünschte, ich müsste es nicht tun.
quelle
Um einer anderen Antwort nicht zuzustimmen: Nein, Sie werfen Funktionen aus, wenn sie mit etwas nicht kompatibel sind, das Sie mehr wollen. Java (und andere GC-Sprachen) haben die explizite Speicherverwaltung verworfen, weil es mehr Typensicherheit wollte. Haskell warf eine Mutation aus, weil er mehr Gleichungsdenken und ausgefallene Typen wollte. Sogar C hat bestimmte Arten von Aliasing und anderem Verhalten verworfen (oder für illegal erklärt), weil es mehr Compiler-Optimierungen wollte.
Die Frage ist also: Was wollen Sie mehr als Vererbung?
quelle
free
/delete
operation können Sie Referenzen ungültig machen. Wenn Ihr Typsystem nicht alle betroffenen Referenzen verfolgen kann, ist die Sprache dadurch unsicher. Insbesondere ist weder C noch C ++ typsicher. Es ist wahr, dass Sie auf die eine oder andere Weise Kompromisse eingehen können (z. B. lineare Typen oder Zuordnungsbeschränkungen), um diese zu vereinbaren. Um genau zu sein, hätte ich sagen sollen, dass Java die Typensicherheit mit einem bestimmten, einfachen Typsystem und einer uneingeschränkten Zuordnung mehr wollte.null
Referenzen zulässt ). Das Verbietenfree
ist eine willkürliche zusätzliche Einschränkung. Zusammenfassend hängt die Frage, ob eine Sprache typsicher ist, mehr von Ihrer Definition der Typensicherheit als von der Sprache ab.T
bezieht sich jede Referenz aufnull
eines oder ein Objekt einer erweiterten KlasseT
.null
ist hässlich, aber Operationen zumnull
Auslösen einer genau definierten Ausnahme, anstatt die oben beschriebene Typinvariante zu beschädigen. Im Gegensatz zu C ++: Nach einem Aufruf vondelete
kann einT*
Zeiger auf einen Speicher verweisen, der keinT
Objekt mehr enthält . Schlimmer noch, wenn Sie eine Feldzuweisung mit diesem Zeiger in einer Zuweisung durchführen, können Sie ein Feld eines Objekts einer anderen Klasse vollständig aktualisieren, wenn es gerade an einer nahe gelegenen Adresse platziert wurde. Das ist keine Typensicherheit nach einer nützlichen Definition des Begriffs.Nein.
Wenn Sie ein Basissprachenfeature entfernen möchten, erklären Sie allgemein, dass es niemals notwendig ist (oder ungerechtfertigt schwierig zu implementieren ist, was hier nicht gilt). Und "nie" ist ein starkes Wort in der Softwareentwicklung. Sie würden eine sehr starke Begründung benötigen, um eine solche Erklärung abzugeben.
quelle
Aus rein C ++ - Sicht ist Vererbung für zwei Hauptzwecke nützlich:
Für Punkt 1 können Sie die Vererbung beseitigen, solange Sie eine Möglichkeit haben, Code zu teilen, ohne sich zu viel Akrobatik hingeben zu müssen. Für Punkt 2. Sie können den Java-Weg gehen und auf einer Schnittstelle bestehen, um diese Funktion zu implementieren.
Die Vorteile der Entfernung der Vererbung sind
Der Kompromiss besteht hauptsächlich zwischen "Nicht wiederholen" und Flexibilität auf der einen Seite und Problemvermeidung auf der anderen Seite. Persönlich würde ich es hassen, wenn die Vererbung von C ++ gehen würde, nur weil ein anderer Entwickler möglicherweise nicht klug genug ist, um die Probleme vorherzusehen.
quelle
Sie können eine Menge sehr nützlicher Arbeit ohne Implementierungsvererbung oder Mixins leisten , aber ich frage mich, ob Sie eine Art Schnittstellenvererbung haben sollten, dh eine Deklaration, die besagt, wenn ein Objekt die Schnittstelle A implementiert, muss es auch die Schnittstelle B (dh) A ist eine Spezialisierung von B und daher gibt es eine Typbeziehung. Auf der anderen Seite muss Ihr resultierendes Objekt nur aufzeichnen, dass es beide Schnittstellen implementiert, sodass es dort nicht zu komplex ist. Alles perfekt machbar.
Das Fehlen einer Vererbung der Implementierung hat jedoch einen klaren Nachteil: Sie können keine numerisch indizierten vtables für Ihre Klassen erstellen und müssen daher für jeden Methodenaufruf Hash-Lookups durchführen (oder einen cleveren Weg finden, um sie zu vermeiden). Dies kann schmerzhaft sein, wenn Sie selbst grundlegende Werte (z. B. Zahlen) durch diesen Mechanismus leiten. Selbst eine sehr gute Hash-Implementierung kann teuer sein, wenn Sie sie in jeder inneren Schleife mehrmals treffen!
quelle