In der objektorientierten Programmierung ist Vererbung der Mechanismus, ein Objekt oder eine Klasse auf einem anderen Objekt ( prototypenbasierte Vererbung ) oder einer anderen Klasse ( klassenbasierte Vererbung ) aufzubauen und dabei eine ähnliche Implementierung beizubehalten . Sie wird auch als Ableitung neuer Klassen ( Unterklassen ) von bestehenden Klassen wie Ober- oder Basisklassen und deren anschließende Bildung einer Klassenhierarchie definiert. In den meisten klassenbasierten objektorientierten Sprachen wie C++ erbt ein durch Vererbung erzeugtes Objekt, ein „Kindobjekt“, alle Eigenschaften und Verhaltensweisen des „Elternobjekts“, mit Ausnahme von: Konstruktoren , Destruktoren, überladenen Operatoren und Freundfunktionen der Basisklasse. Vererbung ermöglicht es Programmierern, Klassen zu erstellen, die auf bestehenden Klassen aufbauen [ 1 ] , eine neue Implementierung zu spezifizieren und dabei die gleichen Verhaltensweisen beizubehalten ( Realisierung einer Schnittstelle ), Code wiederzuverwenden und die ursprüngliche Software unabhängig über öffentliche Klassen und Schnittstellen zu erweitern . Die Beziehungen zwischen Objekten oder Klassen durch Vererbung bilden einen gerichteten azyklischen Graphen .

Eine geerbte Klasse wird als Unterklasse ihrer Elternklasse oder Oberklasse bezeichnet . Der Begriff Vererbung wird im allgemeinen Sprachgebrauch sowohl für klassenbasierte als auch für prototypenbasierte Programmierung verwendet, ist aber im engeren Sinne der klassenbasierten Programmierung vorbehalten (eine Klasse erbt von einer anderen). Die entsprechende Technik in der prototypenbasierten Programmierung wird hingegen als Delegation bezeichnet (ein Objekt delegiert an ein anderes). Klassenmodifizierende Vererbungsmuster können anhand einfacher Netzwerkschnittstellenparameter vordefiniert werden, sodass die Kompatibilität zwischen verschiedenen Programmiersprachen erhalten bleibt. [ 2 ] [ 3 ]

Vererbung sollte nicht mit Subtyping verwechselt werden . [ 4 ] [ 5 ] In einigen Sprachen, insbesondere in statisch typisierten, klassenbasierten OO-Sprachen wie C++ , C# , Java und Scala , stimmen Vererbung und Subtyping überein, in anderen hingegen nicht. Im Allgemeinen etabliert Subtyping eine „ ist-ein“ -Beziehung, während Vererbung lediglich Implementierungen wiederverwendet und eine syntaktische, nicht notwendigerweise eine semantische Beziehung herstellt (Vererbung gewährleistet kein Verhaltens-Subtyping). Um diese Konzepte zu unterscheiden, wird Subtyping manchmal als Schnittstellenvererbung bezeichnet (ohne zu berücksichtigen, dass die Spezialisierung von Typvariablen ebenfalls eine Subtyping-Beziehung induziert), während Vererbung, wie sie hier definiert ist, als Implementierungsvererbung oder Codevererbung bekannt ist . [ 6 ] Dennoch ist Vererbung ein häufig verwendeter Mechanismus zur Etablierung von Subtypbeziehungen. [ 7 ]

Vererbung steht im Gegensatz zur Objektkomposition , bei der ein Objekt ein anderes Objekt enthält (oder Objekte einer Klasse Objekte einer anderen Klasse enthalten); siehe Komposition statt Vererbung . Im Gegensatz zur „ ist-ein “-Beziehung der Subtypisierung implementiert die Komposition eine „ hat-ein“ -Beziehung.

Mathematisch gesehen induziert die Vererbung in jedem Klassensystem eine strikte partielle Ordnung auf der Menge der Klassen in diesem System.

Geschichte

1966 präsentierte Tony Hoare einige Anmerkungen zu Datensätzen, insbesondere zur Idee von Datensatz-Subklassen. Diese Datensatztypen besaßen gemeinsame Eigenschaften, wurden aber durch ein Varianten-Tag unterschieden und hatten Felder, die nur für die jeweilige Variante zugänglich waren. [ 8 ] Davon beeinflusst, stellten Ole-Johan Dahl und Kristen Nygaard 1967 ein Design vor, das die Spezifikation von Objekten verschiedener Klassen mit gemeinsamen Eigenschaften ermöglichte. Die gemeinsamen Eigenschaften wurden in einer Oberklasse zusammengefasst, und jede Oberklasse konnte selbst eine weitere Oberklasse haben. Die Werte einer Subklasse waren somit zusammengesetzte Objekte, bestehend aus mehreren Präfixteilen verschiedener Oberklassen und einem Hauptteil der Subklasse. Diese Teile wurden miteinander verkettet. [ 9 ] Die Attribute eines zusammengesetzten Objekts waren über die Punktnotation zugänglich. Diese Idee wurde erstmals in der Programmiersprache Simula 67 umgesetzt. [ 10 ] Anschließend verbreitete sie sich in Smalltalk , C++ , Java , Python und vielen anderen Sprachen.

Typen

Einfachvererbung
Mehrfachvererbung

Es gibt verschiedene Arten der Vererbung, die auf Paradigmen und spezifischen Sprachen basieren. [ 11 ]

Einfachvererbung
wobei Unterklassen die Merkmale einer Oberklasse erben. Eine Klasse erwirbt die Eigenschaften einer anderen Klasse.
Mehrfachvererbung
wobei eine Klasse mehr als eine Oberklasse haben und Merkmale von allen Oberklassen erben kann.

„Mehrfachvererbung  … galt gemeinhin als sehr schwierig effizient zu implementieren. So behauptete Brad Cox beispielsweise in einer Zusammenfassung von C++ in seinem Buch über Objective-C , dass die Einführung von Mehrfachvererbung in C++ unmöglich sei. Daher schien Mehrfachvererbung eine größere Herausforderung darzustellen. Da ich mich bereits 1982 mit Mehrfachvererbung auseinandergesetzt und 1984 eine einfache und effiziente Implementierungstechnik gefunden hatte, konnte ich der Herausforderung nicht widerstehen. Ich vermute, dies ist der einzige Fall, in dem die Art und Weise den Verlauf der Ereignisse beeinflusst hat.“ [ 12 ]

Mehrstufige Vererbung
Hierbei erbt eine Unterklasse von einer anderen Unterklasse. Es ist nicht ungewöhnlich, dass eine Klasse von einer anderen abgeleiteten Klasse abgeleitet wird, wie in der Abbildung „Mehrstufige Vererbung“ dargestellt.
Mehrstufige Vererbung
Die Klasse A dient als Basisklasse für die abgeleitete Klasse B , welche wiederum als Basisklasse für die abgeleitete Klasse C dient . Die Klasse B wird als Zwischenbasisklasse bezeichnet , da sie die Vererbung zwischen A und C ermöglicht . Die Kette ABC wird als Vererbungspfad bezeichnet .
Eine abgeleitete Klasse mit mehrstufiger Vererbung wird wie folgt deklariert:
// C++-Sprachimplementierung Klasse A { ... }; // Basisklasse Klasse B : public A { ... }; // B abgeleitet von A Klasse C : public B { ... }; // C abgeleitet von B
Dieser Prozess kann auf beliebig viele Ebenen ausgedehnt werden.
Hierarchische Vererbung
Hierbei dient eine Klasse als Oberklasse (Basisklasse) für mehrere Unterklassen. Beispielsweise kann eine Oberklasse A zwei Unterklassen B und C haben. Die Oberklasse von B und C ist jeweils A, aber B und C sind zwei separate Unterklassen.
Hybride Vererbung
Hybride Vererbung liegt vor, wenn zwei oder mehr der oben genannten Vererbungsarten kombiniert werden. Ein Beispiel hierfür ist eine Klasse A mit einer Unterklasse B, die wiederum zwei Unterklassen C und D besitzt. Dies ist eine Mischung aus mehrstufiger und hierarchischer Vererbung.

Unterklassen und Oberklassen

Unterklassen , abgeleitete Klassen , Erbklassen oder Kindklassen sind modulare abgeleitete Klassen, die eine oder mehrere Sprachelemente von einer oder mehreren anderen Klassen ( Oberklassen , Basisklassen oder Elternklassen ) erben. Die Semantik der Klassenvererbung variiert von Sprache zu Sprache, aber üblicherweise erbt die Unterklasse automatisch die Instanzvariablen und Memberfunktionen ihrer Oberklassen.

In C++ lautet die allgemeine Form der Definition einer abgeleiteten Klasse: [ 13 ]

class SubClass : visibility SuperClass { // Unterklassenmitglieder };

Der Doppelpunkt zeigt an, dass die Unterklasse von der Oberklasse erbt. Der Sichtbarkeitsmodifikator ist optional und kann, falls vorhanden, entweder „private“ oder „public“ sein . Die Standardsichtbarkeit (falls kein Modifikator angegeben ist) ist „private“ . Die Sichtbarkeit legt fest, ob die Merkmale der Basisklasse privat oder öffentlich abgeleitet werden .

Beachten Sie, dass es in einigen anderen Sprachen wie Java und C# keinen Sichtbarkeitsmodifikator für Vererbung gibt:

// Kein Sichtbarkeitsmodifikator // Entspricht public SuperClass in C++ class SubClass extends SuperClass { // Unterklassenmitglieder }

Manche Sprachen unterstützen auch die Vererbung anderer Konstrukte. In Eiffel beispielsweise werden Verträge, die die Spezifikation einer Klasse definieren, ebenfalls an die Unterklassen vererbt. Die Oberklasse definiert eine gemeinsame Schnittstelle und grundlegende Funktionalität, die spezialisierte Unterklassen erben, modifizieren und ergänzen können. Die von einer Unterklasse geerbte Software gilt in dieser Unterklasse als wiederverwendet . Ein Verweis auf eine Instanz einer Klasse kann sich tatsächlich auf eine ihrer Unterklassen beziehen. Die tatsächliche Klasse des referenzierten Objekts lässt sich zur Kompilierzeit nicht vorhersagen . Über eine einheitliche Schnittstelle werden die Memberfunktionen von Objekten verschiedener Klassen aufgerufen. Unterklassen können Funktionen der Oberklasse durch völlig neue Funktionen ersetzen, die dieselbe Methodensignatur aufweisen müssen .

Nicht unterteilbare Klassen

In einigen Programmiersprachen kann eine Klasse durch Hinzufügen bestimmter Klassenmodifikatoren als nicht unterteilbar deklariert werden . Beispiele hierfür sind das Schlüsselwort `unclass` in Java und C++11 oder das Schlüsselwort `unclass` in C#. Diese Modifikatoren werden vor dem Schlüsselwort `unclass` und der Klassendeklaration hinzugefügt. Derartige nicht unterteilbare Klassen schränken die Wiederverwendbarkeit ein, insbesondere wenn Entwickler nur Zugriff auf vorkompilierte Binärdateien und nicht auf den Quellcode haben . finalsealedclass

Eine nicht-unterklassenfähige Klasse hat keine Unterklassen. Daher lässt sich bereits zur Kompilierzeit leicht ableiten , dass Referenzen oder Zeiger auf Objekte dieser Klasse tatsächlich auf Instanzen dieser Klasse verweisen und nicht auf Instanzen von Unterklassen (die nicht existieren) oder Instanzen von Oberklassen ( die Typumwandlung eines Referenztyps verletzt das Typsystem). Da der genaue Typ des referenzierten Objekts vor der Ausführung bekannt ist, kann frühe Bindung (auch statische Dispatch genannt ) anstelle von später Bindung (auch dynamische Dispatch genannt ) verwendet werden. Letztere erfordert je nach Unterstützung von Mehrfach- oder Einfachvererbung in der verwendeten Programmiersprache einen oder mehrere Zugriffe auf die virtuelle Methodentabelle .

Nicht überschreibbare Methoden

So wie Klassen nicht vererbbar sein können, können Methodendeklarationen Methodenmodifikatoren enthalten, die verhindern, dass die Methode überschrieben (d. h. in einer Unterklasse durch eine neue Funktion mit demselben Namen und derselben Typsignatur ersetzt) ​​wird. Eine private Methode ist nicht überschreibbar, da sie nur von der Klasse, zu der sie eine Memberfunktion ist, zugänglich ist (dies gilt jedoch nicht für C++). Eine finalMethode in Java, eine sealedMethode in C# oder ein frozenFeature in Eiffel kann nicht überschrieben werden.

Virtuelle Methoden

Ist eine Methode einer Oberklasse virtuell , werden ihre Aufrufe dynamisch ausgeführt . Manche Sprachen (z. B. C++) erfordern die explizite Deklaration einer Methode als virtuell, andere (z. B. Java) setzen voraus, dass alle Methoden virtuell sind. Der Aufruf einer nicht-virtuellen Methode erfolgt stets statisch (d. h. die Adresse des Funktionsaufrufs wird zur Kompilierzeit festgelegt). Statische Ausführung ist schneller als dynamische Ausführung und ermöglicht Optimierungen wie die Inline-Expansion .

Sichtbarkeit geerbter Mitglieder

Die folgende Tabelle zeigt, welche Variablen und Funktionen in Abhängigkeit von der bei der Ableitung der Klasse angegebenen Sichtbarkeit vererbt werden, unter Verwendung der in C++ etablierten Terminologie. [ 14 ]

Sichtbarkeit der Basisklasse Sichtbarkeit der abgeleiteten Klasse
Privater Ableitung Geschützte Ableitung Öffentliche Ableitung
  • Privat →
  • Geschützt →
  • Öffentlich →
  • Nicht vererbt
  • Privat
  • Privat
  • Nicht vererbt
  • Geschützt
  • Geschützt
  • Nicht vererbt
  • Geschützt
  • Öffentlich

Anwendungen

Vererbung dient dazu, zwei oder mehr Klassen miteinander in Beziehung zu setzen.

Überschreiben

Illustration der Methodenüberschreibung

Viele objektorientierte Programmiersprachen erlauben es einer Klasse oder einem Objekt, die Implementierung eines geerbten Aspekts – typischerweise eines Verhaltens – zu ersetzen. Dieser Vorgang wird als Überschreiben bezeichnet . Das Überschreiben führt zu einer Komplikation: Welche Version des Verhaltens verwendet eine Instanz der geerbten Klasse – die der eigenen Klasse oder die der Basisklasse? Die Antwort variiert je nach Programmiersprache, und einige Sprachen bieten die Möglichkeit, anzugeben, dass ein bestimmtes Verhalten nicht überschrieben werden soll und sich wie in der Basisklasse definiert verhalten soll. In C# beispielsweise kann die Basismethode oder -eigenschaft in einer Unterklasse nur dann überschrieben werden, wenn sie mit dem Modifikator `virtual`, `abstract` oder `override` gekennzeichnet ist, während in Programmiersprachen wie Java verschiedene Methoden aufgerufen werden können, um andere Methoden zu überschreiben. [ 15 ] Eine Alternative zum Überschreiben ist das Verbergen des geerbten Codes.

Code-Wiederverwendung

Die Implementierungsvererbung ist der Mechanismus, durch den eine Unterklasse Code einer Basisklasse wiederverwendet . Standardmäßig behält die Unterklasse alle Operationen der Basisklasse bei, sie kann aber auch einige oder alle Operationen überschreiben und die Implementierung der Basisklasse durch ihre eigene ersetzen.

Im folgenden Beispiel überschreiben Unterklassen SquareSumComputerdie CubeSumComputerMethode transform()der Basisklasse SumComputer. Die Basisklasse enthält Operationen zur Berechnung der Summe der Quadrate zweier ganzer Zahlen. Die Unterklasse verwendet die gesamte Funktionalität der Basisklasse mit Ausnahme der Operation, die eine Zahl in ihr Quadrat umwandelt. Diese Operation wird durch eine Operation ersetzt, die eine Zahl in ihr Quadrat bzw. ihren Kubus umwandelt . Die Unterklassen berechnen somit die Summe der Quadrate/Kubus zweier ganzer Zahlen.

Nachfolgend ein Beispiel in Java.

import java.util.List ; import java.util.stream.Collectors ; import java.util.stream.IntStream ;abstract class SumComputer { private int a ; private int b ;public SumComputer ( int a , int b ) { this . a = a ; this . b = b ; }// Abstrakte Methode, die von Unterklassen implementiert werden soll public abstract int transform ( int x );public List <Integer> inputs ( ) { return IntStream.rangeClosed ( a , b ) .boxed.collect ( Collectors.toList ( ) ) ; }​​​​​public int compute () { return inputs (). stream () . mapToInt ( this :: transform ) .sum ( ); } }class SquareSumComputer extends SumComputer { public SquareSumComputer ( int a , int b ) { super ( a , b ); }@Override public int transform ( int x ) { return x * x ; } }class CubeSumComputer extends SumComputer { public CubeSumComputer ( int a , int b ) { super ( a , b ); }@Override public int transform ( int x ) { return x * x * x ; } }

In den meisten Bereichen hat die Klassenvererbung allein zum Zweck der Code-Wiederverwendung an Bedeutung verloren. Hauptgrund dafür ist, dass die Implementierungsvererbung keine polymorphe Austauschbarkeit gewährleistet – eine Instanz der wiederverwendenden Klasse kann nicht unbedingt durch eine Instanz der geerbten Klasse ersetzt werden. Eine alternative Technik, die explizite Delegation , erfordert zwar mehr Programmieraufwand, umgeht aber das Problem der Austauschbarkeit. In C++ kann private Vererbung als eine Form der Implementierungsvererbung ohne Austauschbarkeit verwendet werden. Während öffentliche Vererbung eine „ist-ein“-Beziehung und Delegation eine „hat-ein“-Beziehung darstellt, kann private (und geschützte) Vererbung als eine „wird implementiert in Bezug auf“-Beziehung betrachtet werden. [ 16 ]

Ein weiterer häufiger Anwendungsfall der Vererbung besteht darin, sicherzustellen, dass Klassen eine bestimmte gemeinsame Schnittstelle beibehalten; das heißt, sie implementieren dieselben Methoden. Die Elternklasse kann eine Kombination aus implementierten Operationen und Operationen sein, die in den Kindklassen implementiert werden sollen. Oftmals gibt es keine Änderung der Schnittstelle zwischen Ober- und Unterklasse – die Kindklasse implementiert das beschriebene Verhalten anstelle ihrer Elternklasse. [ 17 ]

Vererbung vs. Subtypisierung

Vererbung ist ähnlich, aber nicht identisch mit Subtyping . [ 4 ] Subtyping ermöglicht es, einen gegebenen Typ durch einen anderen Typ oder eine Abstraktion zu ersetzen. Es wird eine „ist-ein“ -Beziehung zwischen dem Subtyp und einer bestehenden Abstraktion hergestellt, implizit oder explizit, abhängig von der Sprachunterstützung. In Sprachen, die Vererbung als Subtyping-Mechanismus unterstützen, kann diese Beziehung explizit durch Vererbung ausgedrückt werden. Beispielsweise stellt der folgende C++-Code eine explizite Vererbungsbeziehung zwischen den Klassen B und A her , wobei B sowohl eine Unterklasse als auch ein Subtyp von A ist und überall dort als A verwendet werden kann , wo ein B angegeben ist (über eine Referenz, einen Zeiger oder das Objekt selbst).

class A { public : void methodOfA () const { // ... } };class B : public A { public : void methodOfB () const { // ... } };void functionOnA ( const A & a ) { a . methodOfA (); }int main () { B b ; functionOnA ( b ); // b kann durch ein A ersetzt werden. }

In Programmiersprachen, die Vererbung nicht als Subtypisierungsmechanismus unterstützen , besteht die Beziehung zwischen einer Basisklasse und einer abgeleiteten Klasse lediglich zwischen Implementierungen (ein Mechanismus zur Code-Wiederverwendung) und nicht zwischen Typen . Vererbung impliziert selbst in Programmiersprachen, die Vererbung als Subtypisierungsmechanismus unterstützen, nicht zwangsläufig Verhaltenssubtypisierung . Es ist durchaus möglich, eine Klasse abzuleiten, deren Objekt sich in einem Kontext, in dem die Basisklasse erwartet wird, fehlerhaft verhält; siehe das Liskovsche Substitutionsprinzip . [ 18 ] (Vergleiche Konnotation/Denotation .) In einigen objektorientierten Programmiersprachen fallen die Konzepte der Code-Wiederverwendung und der Subtypisierung zusammen, da die einzige Möglichkeit, einen Subtyp zu deklarieren, darin besteht, eine neue Klasse zu definieren, die die Implementierung einer anderen Klasse erbt.

Designbeschränkungen

Die intensive Nutzung von Vererbung beim Entwurf eines Programms bringt gewisse Einschränkungen mit sich.

Betrachten wir beispielsweise eine Klasse, Persondie Name, Geburtsdatum, Adresse und Telefonnummer einer Person enthält. Wir können eine Unterklasse von Person definieren Student, die den Notendurchschnitt und die belegten Kurse der Person enthält, sowie eine weitere Unterklasse von Person namens Mitarbeiter , die die Berufsbezeichnung, den Arbeitgeber und das Gehalt der Person enthält.

Bei der Definition dieser Vererbungshierarchie haben wir bereits bestimmte Einschränkungen festgelegt, von denen nicht alle wünschenswert sind:

  • Einfachvererbung : Bei einfacher Vererbung kann eine Unterklasse nur von einer Oberklasse erben. Um beim obigen Beispiel zu bleiben: Ein PersonObjekt kann entweder ein Objekt vom Typ „a“ Studentoder ein Objekt vom Typ „b“ sein Employee, aber nicht beides. Mehrfachvererbung löst dieses Problem teilweise, da man dann eine StudentEmployeeKlasse definieren kann, die sowohl von „a“ Studentals auch von „b“ erbt Employee. In den meisten Implementierungen kann sie jedoch weiterhin nur einmal von jeder Oberklasse erben und unterstützt daher keine Fälle, in denen ein Student zwei Jobs hat oder zwei Hochschulen besucht. Das in Eiffel verfügbare Vererbungsmodell ermöglicht dies durch die Unterstützung von Mehrfachvererbung .
  • Statisch : Die Vererbungshierarchie eines Objekts ist bei der Instanziierung , wenn der Objekttyp ausgewählt wird, festgelegt und ändert sich nicht mit der Zeit. Beispielsweise erlaubt der Vererbungsgraph nicht, dass ein StudentObjekt zu einem Objekt wird Employeeund dabei den Zustand seiner PersonOberklasse beibehält. (Dieses Verhalten lässt sich jedoch mit dem Dekorator-Muster erreichen .) Vererbung wurde kritisiert, da sie Entwickler an ihre ursprünglichen Designstandards bindet. [ 19 ]
  • Sichtbarkeit : Wenn Clientcode auf ein Objekt zugreift, hat er in der Regel auch Zugriff auf alle Daten der Oberklasse dieses Objekts. Selbst wenn die Oberklasse nicht als öffentlich deklariert wurde, kann der Client das Objekt in den Typ der Oberklasse umwandeln . Beispielsweise ist es nicht möglich, einer Funktion einen Zeiger auf den StudentNotendurchschnitt und das Zeugnis eines Studenten zu übergeben, ohne dieser Funktion gleichzeitig Zugriff auf alle persönlichen Daten in der PersonOberklasse des Studenten zu gewähren. Viele moderne Programmiersprachen, darunter C++ und Java, bieten den Zugriffsmodifizierer „protected“, der es Unterklassen ermöglicht, auf die Daten zuzugreifen, ohne dass Code außerhalb der Vererbungshierarchie darauf zugreifen kann.

Das Prinzip der zusammengesetzten Wiederverwendung ist eine Alternative zur Vererbung. Diese Technik unterstützt Polymorphismus und Code-Wiederverwendung, indem sie Verhaltensweisen von der primären Klassenhierarchie trennt und spezifische Verhaltensklassen nach Bedarf in jede beliebige Klasse der Geschäftsdomäne einbindet. Dieser Ansatz vermeidet die statische Natur einer Klassenhierarchie, indem er Verhaltensänderungen zur Laufzeit ermöglicht und es einer Klasse erlaubt, Verhaltensweisen flexibel zu implementieren, anstatt auf die Verhaltensweisen ihrer Oberklassen beschränkt zu sein.

Probleme und Alternativen

Die Implementierungsvererbung ist unter Programmierern und Theoretikern der objektorientierten Programmierung seit mindestens den 1990er Jahren umstritten. Zu den Kritikern zählen die Autoren von „Design Patterns“ , die stattdessen die Schnittstellenvererbung befürworten und Komposition der Vererbung vorziehen . Beispielsweise wurde das Dekorator-Muster (wie oben erwähnt ) vorgeschlagen, um die statische Natur der Vererbung zwischen Klassen zu überwinden. Als grundlegendere Lösung für dasselbe Problem führt die rollenorientierte Programmierung eine eigenständige Beziehung ein, die durch Rollenspiel entsteht und Eigenschaften von Vererbung und Komposition in einem neuen Konzept vereint.

Laut Allen Holub besteht das Hauptproblem der Implementierungsvererbung darin, dass sie unnötige Kopplungen in Form des „Problems der fragilen Basisklasse“ [ 6 ] einführt : Änderungen an der Implementierung der Basisklasse können unbeabsichtigte Verhaltensänderungen in Unterklassen verursachen. Die Verwendung von Schnittstellen vermeidet dieses Problem, da keine Implementierung, sondern nur die API geteilt wird [ 19 ] . Anders ausgedrückt: „Vererbung bricht die Kapselung “ [ 20 ] . Das Problem tritt besonders deutlich in offenen objektorientierten Systemen wie Frameworks zutage , wo Clientcode von systemseitig bereitgestellten Klassen erben und diese dann in seinen Algorithmen ersetzen soll [ 6 ] .

Berichten zufolge hat sich Java-Erfinder James Gosling gegen die Implementierungsvererbung ausgesprochen und erklärt, er würde sie bei einer Neugestaltung von Java nicht mehr einbeziehen. [ 19 ] Sprachdesigns, die Vererbung und Subtyping entkoppeln (Schnittstellenvererbung), entstanden bereits 1990; [ 21 ] ein modernes Beispiel hierfür ist die Programmiersprache Go .

Komplexe Vererbung oder Vererbung in einem noch nicht ausgereiften Design kann zum Jojo-Problem führen . Als Vererbung Ende der 1990er-Jahre als primärer Ansatz zur Programmstrukturierung genutzt wurde, neigten Entwickler dazu, den Code mit zunehmender Systemfunktionalität in immer mehr Vererbungsebenen aufzuteilen. Kombinierte ein Entwicklungsteam mehrere Vererbungsebenen mit dem Prinzip der Einzelverantwortung, entstanden viele sehr dünne Codeebenen, von denen viele nur aus ein oder zwei Zeilen Code bestanden. Zu viele Ebenen erschweren das Debuggen erheblich, da es schwierig wird, die zu debuggende Ebene zu identifizieren.

Ein weiteres Problem der Vererbung besteht darin, dass Unterklassen im Code definiert werden müssen, was bedeutet, dass Programmbenutzer zur Laufzeit keine neuen Unterklassen hinzufügen können. Andere Entwurfsmuster (wie das Entity-Component-System -Muster ) ermöglichen es Programmbenutzern hingegen, Varianten einer Entität zur Laufzeit zu definieren.

Siehe auch

Referenzen

  1. ^ Johnson, Ralph (26. August 1991). „Designing Reusable Classes“ (PDF) . www.cse.msu.edu .
  2. ^ Madsen, O. L. (1989). „Virtuelle Klassen: Ein leistungsstarker Mechanismus in der objektorientierten Programmierung“. Konferenzbeiträge über objektorientierte Programmiersysteme, Sprachen und Anwendungen – OOPSLA ’89 . S.  397–406 . doi : 10.1145/74877.74919 . ISBN 0897913337. S2CID  1104130 .
  3. ^ Davies, Turk (2021). Fortgeschrittene Methoden und Deep Learning in der Computer Vision . Elsevier Science. S.  179–342 .
  4. Cook , William R.; Hill, Walter; Canning, Peter S. (1990). Vererbung ist nicht Subtyping . Proceedings of the 17th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages ​​(POPL). S.  125–135 . CiteSeerX 10.1.1.102.8635 . doi : 10.1145 /96709.96721 . ISBN  0-89791-343-4Die
  5. ^ Cardelli, Luca (1993). Typeful Programming (Technical report). Digital Equipment Corporation . S. 32–33. SRC Research Report 45.
  6. Mikhajlov , Leonid; Sekerinski, Emil (1998). Eine Studie zum Problem der fragilen Basisklasse (PDF) . Proceedings der 12. Europäischen Konferenz über objektorientierte Programmierung (ECOOP). Lecture Notes in Computer Science. Band 1445. Springer. S.  355–382 . doi : 10.1007 / BFb0054099 . ISBN 978-3-540-64737-9Archiviert vom Original (PDF) am 13.08.2017 . Abgerufen am 28.08.2015 .
  7. Tempero , Ewan; Yang, Hong Yul; Noble, James (2013). Was Programmierer mit Vererbung in Java machen (PDF) . ECOOP 2013 – Objektorientierte Programmierung. Lecture Notes in Computer Science. Band 7920. Springer. S.  577–601 . doi : 10.1007/978-3-642-39038-8_24 . ISBN 978-3-642-39038-8Die
  8. ^ Hoare, CAR (1966). Record Handling (PDF) (Technischer Bericht). S.  15–16 .
  9. ^ Dahl, Ole-Johan ; Nygaard, Kristen (Mai 1967). Klassen- und Unterklassendeklarationen (PDF) . IFIP Working Conference on Simulation Languages. Oslo: Norwegisches Rechenzentrum.
  10. ^ Dahl, Ole-Johan (2004). „Die Geburt der Objektorientierung: Die Simula-Sprachen“ (PDF) . Von der Objektorientierung zu formalen Methoden . Lecture Notes in Computer Science. Band 2635. S.  15–25 . doi : 10.1007/978-3-540-39993-3_3 . ISBN 978-3-540-21366-6Die
  11. ^ „C++ Vererbung“ . www.cs.nmsu.edu . Archiviert vom Original am 24.09.2023 . Abgerufen am 16.05.2018 .
  12. ^ Stroustrup, Bjarne (1994). The Design and Evolution of C++ . Pearson. S. 417. ISBN 9780135229477Die
  13. ^ Schildt, Herbert (2003). The complete reference C++ . Tata McGraw Hill. S.  417 . ISBN 978-0-07-053246-5Die
  14. ^ Balagurusamy, E. (2010). Objektorientierte Programmierung mit C++ . Tata McGraw Hill. S. 213. ISBN 978-0-07-066907-9Die
  15. ^ override(C#-Referenz)
  16. ^ „GotW #60: Ausnahmesicheres Klassendesign, Teil 2: Vererbung“ . Gotw.ca. Abgerufen am 15.08.2012 .
  17. ^ Venugopal, KR; Buyya, Rajkumar (2013). C++ beherrschen . Tata McGraw Hill Education Private Limited. P. 609. ISBN 9781259029943Die
  18. ^ Mitchell, John (2002). „10 Konzepte in objektorientierten Sprachen“Konzepte der Programmiersprache . Cambridge University Press. S.  287. ISBN 978-0-521-78098-8Die
  19. ^ a b c Holub, Allen (1. August 2003). „Warum Erweiterungen böse sind“ . Archiviert vom Original am 24. Februar 2019. Abgerufen am 10. März 2015 .
  20. Seiter , Linda M.; Palsberg, Jens; Lieberherr, Karl J. (1996). „Evolution of object behavior using context relations“ . ACM SIGSOFT Software Engineering Notes . 21 (6): 46. CiteSeerX 10.1.1.36.5053 . doi : 10.1145/250707.239108 . 
  21. ^ America, Pierre (1991). Entwurf einer objektorientierten Programmiersprache mit Verhaltenssubtypisierung . REX School/Workshop on the Foundations of Object-Oriented Languages. Lecture Notes in Computer Science. Band 489. S.  60–90 . doi : 10.1007/BFb0019440 . ISBN 978-3-540-53931-5Die

Weiterführende Literatur