Hoffentlich nicht zu akademisch ...
Angenommen, ich benötige reelle und komplexe Zahlen in meiner SW-Bibliothek.
Basierend auf der is-a- Beziehung (oder hier ) ist die reelle Zahl eine komplexe Zahl, wobei b im Imaginärteil der komplexen Zahl einfach 0 ist.
Auf der anderen Seite wäre meine Implementierung, dass das Kind das Elternteil erweitert, so dass ich in der übergeordneten RealNumber einen Realteil hätte und das Kind ComplexNumber imaginäre Kunst hinzufügen würde.
Es gibt auch eine Meinung, dass Vererbung böse ist .
Ich erinnere mich wie gestern, als ich an der Universität OOP lernte, mein Professor sagte, dies sei kein gutes Beispiel für Vererbung, da der absolute Wert dieser beiden unterschiedlich berechnet wird (aber dafür haben wir Methodenüberladung / Polymorfismus, oder?). .
Ich habe die Erfahrung gemacht, dass wir häufig Vererbung verwenden, um DRY zu lösen. Daher haben wir häufig künstliche abstrakte Klassen in der Hierarchie (wir haben oft Probleme, Namen zu finden, da sie keine Objekte aus einer realen Welt darstellen).
quelle
Antworten:
Auch wenn eine reelle Zahl im mathematischen Sinne eine komplexe Zahl ist, ist es keine gute Idee, reelle aus komplexen Zahlen abzuleiten. Es verstößt gegen das Liskov-Substitutionsprinzip und besagt (unter anderem), dass eine abgeleitete Klasse die Eigenschaften einer Basisklasse nicht verbergen sollte.
In diesem Fall müsste eine reelle Zahl den Imaginärteil der komplexen Zahl verbergen. Es ist klar, dass es keinen Sinn macht, eine versteckte Gleitkommazahl (Imaginärteil) zu speichern, wenn Sie nur den Realteil benötigen.
Dies ist im Grunde das gleiche Problem wie das in einem Kommentar erwähnte Rechteck / Quadrat-Beispiel.
quelle
Dies ist eigentlich kein zwingender Grund gegen jede Vererbung, sondern nur das vorgeschlagene
class RealNumber
<->class ComplexNumber
Modell.Sie könnten vernünftigerweise eine Schnittstelle definieren
Number
, die sowohl implementiertRealNumber
alsComplexNumber
auch implementiert wird.Das könnte so aussehen
Aber dann möchten Sie die anderen
Number
Parameter in diesen Operationen auf denselben abgeleiteten Typ wie beschränkenthis
, mit dem Sie sich nähern könnenOder Sie würden stattdessen eine Sprache verwenden, die strukturellen Polymorphismus anstelle von Subtyp-Polymorphismus zulässt. Für den speziellen Fall von Zahlen benötigen Sie möglicherweise nur die Fähigkeit, arithmetische Operatoren zu überladen.
quelle
Lösung: Haben Sie keine öffentliche
RealNumber
KlasseIch würde es völlig in Ordnung finden, wenn
ComplexNumber
es eine statische Factory-MethodefromDouble(double)
gäbe, die eine komplexe Zahl mit einer imaginären Null zurückgibt. Sie können dann alle Vorgänge verwenden, die Sie für eineRealNumber
Instanz dieserComplexNumber
Instanz verwenden würden.Aber ich habe Probleme zu verstehen, warum Sie eine öffentlich geerbte
RealNumber
Klasse haben möchten / müssen . Normalerweise wird die Vererbung aus diesen Gründen verwendet (aus meinem Kopf heraus korrigieren Sie mich, wenn Sie etwas verpasst haben)Erweiterung des Verhaltens.
RealNumbers
kann keine zusätzlichen Operationen ausführen komplexe Zahlen können nicht ausführen, daher macht es keinen Sinn, dies zu tun.Implementieren von abstraktem Verhalten mit einer bestimmten Implementierung. Da dies
ComplexNumber
nicht abstrakt sein sollte, gilt dies auch nicht.Wiederverwendung von Code. Wenn Sie nur die
ComplexNumber
Klasse verwenden, verwenden Sie 100% des Codes wieder.spezifischere / effizientere / genauere Implementierung für eine bestimmte Aufgabe. Dies könnte hier angewendet werden,
RealNumbers
könnte einige Funktionen schneller implementieren. Aber dann sollte diese Unterklasse hinter der Statik verborgen seinfromDouble(double)
und außerhalb nicht bekannt sein. Auf diese Weise müsste der Imaginärteil nicht ausgeblendet werden. Für die Außenseite sollte es nur komplexe Zahlen geben (welche reellen Zahlen sind). Sie können diese private RealNumber-Klasse auch von allen Operationen in der komplexen Zahlenklasse zurückgeben, die zu einer reellen Zahl führen. (Dies setzt voraus, dass die Klassen wie die meisten Zahlenklassen unveränderlich sind.)Es ist so, als würde man eine Unterklasse von Integer implementieren, die Zero heißt, und einige der Operationen fest codieren, da sie für Zero trivial sind. Sie können dies tun, da jede Null eine Ganzzahl ist, aber nicht öffentlich machen, sondern hinter einer Factory-Methode verstecken.
quelle
Zu sagen, dass eine reelle Zahl eine komplexe Zahl ist, hat in der Mathematik, insbesondere in der Mengenlehre, mehr Bedeutung als in der Informatik.
In der Mathematik sagen wir:
Dies bedeutet jedoch nicht, dass Sie beim Entwerfen Ihrer Bibliothek die Vererbung verwenden müssen oder sollten, um eine RealNumber- und eine ComplexNumber-Klasse einzuschließen. In Effective Java, Zweite Ausgabe von Joshua Bloch; Punkt 16 lautet "Komposition gegenüber Vererbung bevorzugen". Um die in diesem Element genannten Probleme zu vermeiden, kann Ihre RealNumber-Klasse nach der Definition in Ihrer ComplexNumber-Klasse verwendet werden:
Auf diese Weise können Sie Ihre RealNumber-Klasse wiederverwenden, um Ihren Code trocken zu halten und gleichzeitig die von Joshua Bloch identifizierten Probleme zu vermeiden.
quelle
Hier gibt es zwei Probleme. Das erste ist, dass es üblich ist, dieselben Begriffe für die Arten von Containern und deren Inhaltstypen zu verwenden, insbesondere bei primitiven Typen wie Zahlen. Der Begriff
double
wird beispielsweise verwendet, um sowohl einen Gleitkommawert mit doppelter Genauigkeit als auch einen Container zu beschreiben, in dem einer gespeichert werden kann.Das zweite Problem ist, dass sich Beziehungen zwischen Containern, aus denen verschiedene Objekttypen gelesen werden können, genauso verhalten wie die Beziehungen zwischen den Objekten selbst, während sich Beziehungen zwischen Containern, in die verschiedene Objekttypen eingefügt werden können, gegenüber denen zwischen ihren Inhalten verhalten . Jeder Käfig, von dem bekannt ist, dass er eine Instanz von enthält
Cat
, ist ein Käfig, der eine Instanz von enthältAnimal
, muss aber kein Käfig sein, der eine Instanz von enthältSiameseCat
. Andererseits ist jeder Käfig, der alle Instanzen von aufnehmenCat
kann, ein Käfig, der alle Instanzen von aufnehmen kannSiameseCat
, muss aber kein Käfig sein, der alle Instanzen von aufnehmen kannAnimal
. Die einzige Art von Käfig, die alle Instanzen von aufnehmenCat
kann und garantiert werden kann, enthält niemals etwas anderes als eine Instanz vonCat
ist ein Käfig vonCat
. Jede andere Art von Käfig wäre entweder nicht in der Lage, einige Fälle zuCat
akzeptieren, die sie akzeptieren sollte, oder wäre in der Lage, Dinge zu akzeptieren, die keine Fälle sindCat
.quelle