it-swarm.com.de

Wie implementiere ich die Vererbung von RealNumber und ComplexNumber?

Hoffentlich nicht zu akademisch ...

Angenommen, ich benötige reelle und komplexe Zahlen in meiner SW-Bibliothek.

Basierend auf der Beziehung is-a (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, sodass 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 ist böse .

Ich erinnere mich wie gestern, als ich an der Universität lernte OOP an der Universität, sagte mein Professor, dies ist kein gutes Beispiel für Vererbung, da der absolute Wert dieser beiden unterschiedlich berechnet wird (aber dafür haben wir eine Methode Ü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).

11
Betlista

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.

17
Frank Puffer

kein gutes Beispiel für Vererbung, da der absolute Wert dieser beiden Werte unterschiedlich berechnet wird

Dies ist eigentlich kein zwingender Grund gegen alle Vererbung hier, nur das vorgeschlagene class RealNumber <-> class ComplexNumber Modell.

Sie könnten vernünftigerweise eine Schnittstelle Number definieren, die beideRealNumber und ComplexNumber implementieren würden.

Das könnte so aussehen

interface Number
{
    Number Add(Number rhs);
    Number Subtract(Number rhs);
    // ... etc
}

Aber dann möchten Sie die anderen Number -Parameter in diesen Operationen auf denselben abgeleiteten Typ wie this beschränken, mit dem Sie sich nähern können

interface Number<T>
{
    Number<T> Add(Number<T> rhs);
    Number<T> Subtract(Number<T> rhs);
    // ... etc
}

Oder Sie würden stattdessen eine Sprache verwenden, die strukturellen Polymorphismus anstelle von Subtyp-Polymorphismus zulässt. Für die Sonderfall von Zahlen benötigen Sie möglicherweise nur die Fähigkeit, arithmetische Operatoren zu überladen.

complex operator + (complex lhs, complex rhs);
complex operator - (complex lhs, complex rhs);
// ... etc

Number frobnicate<Number>(List<Number> foos, Number bar); // uses arithmetic operations
3
Caleth

Lösung: Haben Sie keine öffentliche RealNumber Klasse

Ich würde es völlig in Ordnung finden, wenn ComplexNumber eine statische Factory-Methode fromDouble(double) hätte, die eine komplexe Zahl zurückgeben würde, wobei imaginär Null ist. Sie können dann alle Operationen verwenden, die Sie für eine RealNumber -Instanz in dieser ComplexNumber -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, die komplexe Zahlen nicht ausführen können. Es macht also keinen Sinn, dies zu tun.

  • implementieren von abstraktem Verhalten mit einer bestimmten Implementierung. Da ComplexNumber nicht abstrakt sein sollte, gilt dies auch nicht.

  • wiederverwendung von Code. Wenn Sie nur die Klasse ComplexNumber 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 statischen fromDouble(double) versteckt sein 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.

0
findusl

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:

  • Eine reelle Zahl ist eine komplexe Zahl, da die Menge der komplexen Zahlen die Menge der reellen Zahlen enthält.
  • Eine rationale Zahl ist eine reelle Zahl, da die Menge der reellen Zahlen die Menge der rationalen Zahlen (und die Menge der irrationalen Zahlen) enthält.
  • Eine Ganzzahl ist eine rationale Zahl, da die Menge der rationalen Zahlen die Menge der Ganzzahlen enthält.

Dies bedeutet jedoch nicht, dass Sie beim Entwerfen Ihrer Bibliothek eine Vererbung verwenden müssen oder sollten, um eine RealNumber- und eine ComplexNumber-Klasse einzuschließen. In Effective Java, Second Edition von Joshua Bloch; Punkt 16 ist "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:

public class ComplexNumber {
    private RealNumber realPart;
    private RealNumber imaginaryPart;

    // Implementation details are for you to write
}

Auf diese Weise können Sie Ihre RealNumber-Klasse wiederverwenden, um Ihren Code DRY] beizubehalten und gleichzeitig die von Joshua Bloch identifizierten Probleme zu vermeiden.

0
Craig Noah

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 Cat enthält, ist ein Käfig, der eine Instanz von Animal enthält, muss jedoch kein Käfig sein, der eine Instanz von SiameseCat enthalten kann, ein Käfig, der alle Instanzen von Cat enthalten kann, muss jedoch kein Käfig sein, der alle Instanzen von SiameseCat enthalten kann Animal. Die einzige Art von Käfig, die alle Instanzen von Cat aufnehmen kann und garantiert niemals etwas anderes als eine Instanz von Cat enthalten kann, ist ein Käfig von Cat. Jede andere Art von Käfig wäre entweder nicht in der Lage, einige Instanzen von Cat zu akzeptieren, die sie akzeptieren sollte, oder wäre in der Lage, Dinge zu akzeptieren, die keine Instanzen von Cat sind.

0
supercat