Um zu verstehen, wie man die Mehrfachvererbungsprobleme von Java löst, habe ich eine klassische Frage, die geklärt werden muss.
Nehmen wir an, ich habe eine Klasse, Animal
die Unterklassen hat, Bird
und Horse
ich muss eine Klasse machen Pegasus
, die sich von Bird
und Horse
nach Pegasus
sowohl einem Vogel als auch einem Pferd erstreckt.
Ich denke, das ist das klassische Diamantproblem. Von dem, was ich die klassische Art und Weise versteht , kann dies zu lösen , ist das zu machen Animal
, Bird
und Horse
Klassen - Schnittstellen und implementieren Pegasus
von ihnen.
Ich habe mich gefragt, ob es einen anderen Weg gibt, um das Problem zu lösen, bei dem ich noch Objekte für Vögel und Pferde erstellen kann. Wenn es einen Weg gäbe, auch Tiere erschaffen zu können, wäre das großartig, aber nicht notwendig.
public class Pegasus extends Horse implements Flying
.Antworten:
Sie könnten Schnittstellen für Tierklassen (Klasse in der biologischen Bedeutung) erstellen, z. B.
public interface Equidae
für Pferde undpublic interface Avialae
Vögel (ich bin kein Biologe, daher können die Begriffe falsch sein).Dann können Sie noch eine erstellen
und
und auch
Hinzufügen aus den Kommentaren:
Um doppelten Code zu reduzieren, können Sie eine abstrakte Klasse erstellen, die den größten Teil des allgemeinen Codes der Tiere enthält, die Sie implementieren möchten.
Aktualisieren
Ich möchte noch ein Detail hinzufügen. Wie Brian bemerkt , wusste das OP dies bereits.
Ich möchte jedoch betonen, dass ich vorschlage, das Problem der "Mehrfachvererbung" mit Schnittstellen zu umgehen, und dass ich nicht empfehle, Schnittstellen zu verwenden, die bereits einen konkreten Typ darstellen (wie z. B. Bird), sondern eher ein Verhalten (auf das sich andere beziehen) Ententypisierung, was auch gut ist, aber ich meine nur: die biologische Klasse der Vögel, Avialae). Ich empfehle auch nicht, Schnittstellennamen zu verwenden, die mit einem Großbuchstaben 'I' beginnen, z. B.
IBird
was nur nichts darüber aussagt, warum Sie eine Schnittstelle benötigen. Das ist der Unterschied zu der Frage: Erstellen Sie die Vererbungshierarchie mithilfe von Schnittstellen, verwenden Sie bei Bedarf abstrakte Klassen, implementieren Sie bei Bedarf konkrete Klassen und verwenden Sie gegebenenfalls die Delegierung.quelle
AbstractHorse
, mit dem auch Zebras oder andere pferdeartige Tiere gebaut werden können.AbstractEquidae
möchten , ist dies wahrscheinlich besser geeignet alsAbstractHorse
. Es wäre seltsam, wenn ein Zebra ein abstraktes Pferd ausstrecken würde. Übrigens eine gute Antwort.Duck
Implementieren einer Klasse beinhaltenAvialae
?Es gibt zwei grundlegende Ansätze zum Kombinieren von Objekten:
Dies funktioniert so, dass Sie ein Tierobjekt haben. Innerhalb dieses Objekts fügen Sie dann weitere Objekte hinzu, die die gewünschten Eigenschaften und Verhaltensweisen angeben.
Beispielsweise:
Jetzt
IFlier
sieht es einfach so aus:Bird
Sieht also so aus:Jetzt haben Sie alle Vorteile der Vererbung. Sie können Code wiederverwenden. Sie können eine Sammlung von IFliers haben und alle anderen Vorteile des Polymorphismus usw. nutzen.
Sie haben jedoch auch die Flexibilität von Composition. Sie können so viele verschiedene Schnittstellen und zusammengesetzte Hintergrundklassen auf jeden Typ anwenden
Animal
- mit so viel Kontrolle, wie Sie über die Einrichtung der einzelnen Bits benötigen.Strategiemuster alternativer Ansatz zur Komposition
Ein alternativer Ansatz, der davon abhängt, was und wie Sie tun, besteht darin, dass die
Animal
Basisklasse eine interne Sammlung enthält, um die Liste der verschiedenen Verhaltensweisen zu führen. In diesem Fall verwenden Sie am Ende etwas, das näher am Strategiemuster liegt. Das bietet Vorteile in Bezug auf die Vereinfachung des Codes (zum BeispielHorse
muss nichts überQuadruped
oder bekannt seinHerbivore
), aber wenn Sie nicht auch den Schnittstellenansatz verwenden, verlieren Sie viele Vorteile des Polymorphismus usw.quelle
getFlier()
hat für jeden und jede Art von Vogel neu implementiert werden.MathsTeacher
undEnglishTeacher
beide erbenTeacher
,ChemicalEngineer
,MaterialsEngineer
etc vererbenEngineer
.Teacher
undEngineer
beide implementierenComponent
. Das hatPerson
dann nur eine Liste vonComponent
s, und Sie können ihnen die richtigenComponent
s dafür gebenPerson
. dhperson.getComponent(Teacher.class)
,person.getComponent(MathsTeacher.class)
usw.Ich habe eine blöde Idee:
quelle
Darf ich das Konzept der Ententypisierung vorschlagen ?
Höchstwahrscheinlich würden Sie dazu neigen, den Pegasus dazu zu bringen, eine Vogel- und eine Pferdeschnittstelle zu erweitern, aber die Eingabe von Enten legt tatsächlich nahe, dass Sie lieber Verhalten erben sollten . Wie bereits in den Kommentaren erwähnt, ist ein Pegasus kein Vogel, aber er kann fliegen. Ihr Pegasus sollte also lieber eine
Flyable
Schnittstelle erben und sagen wir eineGallopable
Schnittstelle.Diese Art von Konzept wird im Strategiemuster verwendet . Das gegebene Beispiel zeigt Ihnen , tatsächlich wie eine Ente erbt das
FlyBehaviour
undQuackBehaviour
und dort noch Enten sein kann, zum Beispiel dieRubberDuck
, die nicht fliegen kann. Sie hätten dieDuck
Erweiterung auch zu einerBird
Klasse machen können, aber dann hätten sie etwas Flexibilität aufgegeben, weil jederDuck
fliegen könnte, auch die ArmenRubberDuck
.quelle
Technisch gesehen können Sie jeweils nur eine Klasse erweitern und mehrere Schnittstellen implementieren. Wenn Sie jedoch die Softwareentwicklung in die Hand nehmen, würde ich eher eine problemspezifische Lösung vorschlagen, die im Allgemeinen nicht zu beantworten ist. Übrigens ist es eine gute OO-Praxis, konkrete Klassen nicht zu erweitern / nur abstrakte Klassen zu erweitern, um unerwünschtes Vererbungsverhalten zu verhindern - es gibt kein "Tier" und keine Verwendung eines Tierobjekts, sondern nur konkrete Tiere.
quelle
In Java 8, das sich seit Februar 2014 noch in der Entwicklungsphase befindet, können Sie Standardmethoden verwenden , um eine Art C ++ - wie Mehrfachvererbung - zu erreichen. Sie können sich auch dieses Tutorial ansehen, das einige Beispiele zeigt, mit denen Sie leichter arbeiten können als mit der offiziellen Dokumentation.
quelle
Es ist sicher, ein Pferd in einem Stall mit einer halben Tür zu halten, da ein Pferd nicht über eine halbe Tür gelangen kann. Deshalb habe ich einen Pferdehausdienst eingerichtet, der alle Gegenstände vom Typ Pferd akzeptiert und in einen Stall mit einer halben Tür stellt.
Ist ein Pferd wie ein Tier, das sogar ein Pferd fliegen kann?
Früher habe ich viel über Mehrfachvererbung nachgedacht, aber jetzt, da ich seit über 15 Jahren programmiere, ist es mir nicht mehr wichtig, Mehrfachvererbung zu implementieren.
Meistens, wenn ich versucht habe, mit einem Design umzugehen, das auf Mehrfachvererbung hinweist, habe ich später veröffentlicht, dass ich die Problemdomäne nicht verstanden habe.
ODER
quelle
Java hat kein Mehrfachvererbungsproblem, da es keine Mehrfachvererbung hat. Dies ist beabsichtigt, um das eigentliche Problem der Mehrfachvererbung (das Diamantproblem) zu lösen.
Es gibt verschiedene Strategien zur Minderung des Problems. Das am schnellsten erreichbare ist das von Pavel vorgeschlagene zusammengesetzte Objekt (im Wesentlichen, wie C ++ damit umgeht). Ich weiß nicht, ob für die Zukunft von Java eine Mehrfachvererbung über C3-Linearisierung (oder ähnliches) geplant ist, aber ich bezweifle dies.
Wenn Ihre Frage akademisch ist, ist die richtige Lösung, dass Vogel und Pferd konkreter sind, und es ist falsch anzunehmen, dass ein Pegasus einfach ein Vogel und ein Pferd zusammen ist. Es wäre richtiger zu sagen, dass ein Pegasus bestimmte intrinsische Eigenschaften gemeinsam mit Vögeln und Pferden hat (das heißt, sie haben möglicherweise gemeinsame Vorfahren). Dies kann ausreichend modelliert werden, wie Moritz 'Antwort hervorhebt.
quelle
Ich denke, es hängt sehr stark von Ihren Bedürfnissen ab und davon, wie Ihre Tierklassen in Ihrem Code verwendet werden sollen.
Wenn Sie Methoden und Funktionen Ihrer Pferde- und Vogelimplementierungen in Ihrer Pegasus-Klasse nutzen möchten, können Sie Pegasus als Komposition aus einem Vogel und einem Pferd implementieren :
Eine andere Möglichkeit ist die Verwendung eines Entity-Component-System- Ansatzes anstelle der Vererbung zur Definition Ihrer Tiere. Dies bedeutet natürlich, dass Sie keine einzelnen Java-Klassen der Tiere haben, sondern diese nur durch ihre Komponenten definiert werden.
Ein Pseudocode für einen Entity-Component-System-Ansatz könnte folgendermaßen aussehen:
quelle
Sie können eine Schnittstellenhierarchie haben und dann Ihre Klassen von ausgewählten Schnittstellen aus erweitern:
und definieren Sie dann Ihre Klassen nach Bedarf, indem Sie eine bestimmte Schnittstelle erweitern:
quelle
IBird
undIHorse
sollteIAnimal
anstelle vonAnimal
Ähm, Ihre Klasse kann die Unterklasse für nur eine andere sein, aber Sie können trotzdem so viele Schnittstellen implementieren, wie Sie möchten.
Ein Pegasus ist in der Tat ein Pferd (es ist ein Sonderfall eines Pferdes), das fliegen kann (was die "Fähigkeit" dieses besonderen Pferdes ist). Auf der anderen Seite kann man sagen, dass der Pegasus ein Vogel ist, der laufen kann und vierbeinig ist - alles hängt davon ab, wie einfach es für Sie ist, den Code zu schreiben.
Wie in Ihrem Fall können Sie sagen:
quelle
Schnittstellen simulieren keine Mehrfachvererbung. Java-Ersteller hielten Mehrfachvererbung für falsch, daher gibt es in Java keine solche.
Wenn Sie die Funktionalität von zwei Klassen zu einer kombinieren möchten, verwenden Sie die Objektzusammensetzung. Dh
Wenn Sie bestimmte Methoden verfügbar machen möchten, definieren Sie sie und lassen Sie sie den Aufruf an den entsprechenden Controller delegieren.
Hier können Schnittstellen nützlich sein - wenn Sie
Component1
SchnittstelleInterface1
undComponent2
Geräte implementierenInterface2
, können Sie diese definierenDamit Sie Objekte austauschbar verwenden können, wo es der Kontext zulässt.
Aus meiner Sicht kann man also nicht in das Diamantproblem geraten.
quelle
Wie Sie bereits wissen, ist eine Mehrfachvererbung von Klassen in Java nicht möglich, jedoch mit Schnittstellen. Möglicherweise möchten Sie auch das Kompositionsmuster verwenden.
Ich habe vor ein paar Jahren einen sehr umfassenden Artikel über Komposition geschrieben ...
/codereview/14542/multiple-inheritance-and-composition-with-java-and-c-updated
quelle
Schauen Sie sich zum besseren Verständnis das folgende Beispiel an
Wann sollte das Dekorationsmuster verwendet werden?
quelle
Um die Komplexität zu verringern und die Sprache zu vereinfachen, wird die Mehrfachvererbung in Java nicht unterstützt.
Stellen Sie sich ein Szenario vor, in dem A, B und C drei Klassen sind. Die C-Klasse erbt die Klassen A und B. Wenn A- und B-Klassen dieselbe Methode haben und Sie sie vom untergeordneten Klassenobjekt aufrufen, besteht Mehrdeutigkeit beim Aufrufen der Methode der A- oder B-Klasse.
Da Kompilierungsfehler besser sind als Laufzeitfehler, rendert Java Kompilierungsfehler, wenn Sie zwei Klassen erben. Unabhängig davon, ob Sie dieselbe oder eine andere Methode verwenden, tritt jetzt ein Fehler bei der Kompilierung auf.
quelle
Um das Problem der Mehrfachvererbung in Java zu lösen, wird die → Schnittstelle verwendet
J2EE (Core JAVA) Notizen von Herrn KVR Seite 51
quelle