Is a beziehung
-
Eine Ellipse kann man zB in X oder Y-Richtung skalieren, was beim Kreis keinen Sinn macht (da er dann kein Kreis mehr wäre).
Aber das wird doch sicherlich in deinem Buch oder what ever dabei stehen!?
Da steht doch nicht einfach "Leite Kreis nicht von Ellipse ab".
-
blurry333 schrieb:
Warum soll man den Kreis nicht von der Klasse Ellipse ableiten obwohl Kreis ein spezialfall von Ellipse ist.
Grundsätzlich sollte eine "ist ein" Beziehung verwendet werden, wenn die abgeleitete Klasse auch vom Sinn komplett mit der Oberklasse übereinstimmt. Eine Ellipse ist aber mehr als ein Kreis.
Umgekehrt könnte man nicht überlegen ob man Ellipsen auch für Kreise verwenden, und ihnen ein paar Zusatzfunktionen (IstKreis...) spendiert.
-
blurry333 schrieb:
class Ellipse { }; class Kreis:public Ellipse{};Warum soll man den Kreis nicht von der Klasse Ellipse ableiten obwohl
Kreis ein spezialfall von Ellipse ist.Die Generalisierung von Ellipse -> Kreis verstößt gegen das LSP.
Edit:
Im deutschen Artikel wird das Problem sogar explizit erwähnt und nochmals zum Kreis-Ellipse-Problem weiterverwiesen.
-
David_pb schrieb:
Die Generalisierung von Ellipse -> Kreis verstößt gegen das LSP.
Schreib doch bitte Wörter statt grafischer Nichtstandardabkürzungen, wenn die gesamte Aussage darauf liegt.
Heißt A -> B bei Dir A erbt von B oder B erbt von A?
-
volkard schrieb:
David_pb schrieb:
Die Generalisierung von Ellipse -> Kreis verstößt gegen das LSP.
Schreib doch bitte Wörter statt grafischer Nichtstandardabkürzungen, wenn die gesamte Aussage darauf liegt.
Heißt A -> B bei Dir A erbt von B oder B erbt von A?Sorry, ich dachte, dass Generalisierung klar macht was gemeint ist (Der Pfeil war eigentlich nur als Andeutung gedacht, dass ein Zusammenhang zwischen A und B besteht. Allerdings ist der Pfeil bzgl UML Notation falsch rum, stimmt ;)). Es soll natürlich heissen Kreis erbt von Ellipse.
-
(A erbt von
== (A ->
== (B <- A) == B ist die Generalisierung von A.
(A erbt von
== (A <-
== (B -> A) == A ist eine Spezilisierung von B.Siehst du etwas?
-
Ob nun Kreis von Ellipse oder Ellipse von Kreis abgeleitet wird, man kann in beiden Fällen mit dem LSP argumentieren, dass keine korrekte Subtypenbeziehung besteht.
-
Kreis erbt von Ellipse
Ellipse stellt Methoden zur Verfügung, um die beiden "Radien" unabhängig voneinander zu manipulieren, das Gebilde ist damit kein Kreis mehr. Kreis kann diese Methoden überschreiben, dann geht aber das zugesicherte Verhalten von Ellipse kaputt. So oder so: Kein Subtyp. -
Ellipse erbt von Kreis
Genau dasselbe, nur umgekehrt. Bei einem Kreis kann ich davon ausgehen, dass er kreisrund ist. Ellipsen können aber ihre "Radien" unabhängig voneinander setzen, damit ist diese Zusicherung verletzt.
Dass ein Kreis eine Ellipse ist, stimmt nur für unveränderliche Objekte. Überhaupt kann man schnell drauf kommen, dass die "ist-ein"-Beziehung hier überstrapaziert wird, denn das Subtyping bezieht sich auf das Verhalten, aber was ist denn das Verhalten eines Kreises, und was hat es mit dem Verhalten einer Ellipse zu tun? Der vorschnelle Schluss, dass ein Kreis eine Ellipse sei, kommt aus der Mathematik und somit von verhaltenslosen Objekten.
-
-
Zeus schrieb:
(A erbt von
== (A ->
== (B <- A) == B ist die Generalisierung von A.
(A erbt von
== (A <-
== (B -> A) == A ist eine Spezilisierung von B.Siehst du etwas?
Ja, wenn man sich strikt nach UML Notation orientiert. Wie oben bereits erläutert war der Pfeil nicht in diesem Sinne gedacht und deshalb hab ich ja, um allen Missverständnissen vorzubeugen, explizit noch mal erkäutert was gemeint war. Weiterhin dürfte aus dem Kontext des Threads und den geposteten Links klar sein was gemeint war und im Endeffekt ist die Richtung in diesem Beispiel ohnehin irrelevant...
-
Bashar schrieb:
Dass ein Kreis eine Ellipse ist, stimmt nur für unveränderliche Objekte.
Genau dazu wollte ich gerade was schreiben: wenn man die Objekte "immutable" macht, also unveränderlich, dann kann man Kreis von Ellipse ableiten.
-
class Ellipse{ public: setRadius(int x) setRadius(int y) }; class Kreis:public Ellipse { setRadius(int x) setRadius(int y) };Ich habs immer noch net ganz verstanden. Ist das Problem folgendes:
Ellipse braucht 2 funktionen zum Setzen der Halbachsen.
Der Kreis braucht allerdings nur 1 Funktion , nämlich setzen des Radius.
Trotzdem muss er beide überschreiben obwohl er ja nur eine braucht.Und das ist wohl dann das Problem ?
-
Autsch. Du musst den Methoden unterschiedliche Namen geben, wie du die Parameter nennst, ist egal.
Das Problem ist dann Folgendes:
Ellipse e; Kreis k; e.width = e.height = 10; k.width = k.height = 10; e.width = 20; k.width = 20;Dann ist e.height = 10, aber k.height = 20. Also verhält sich ein Kreis nicht wie eine Ellipse.
-
Ein "gutes" Beispiel:
Ein Kraftfahrzeug kann man starten und man kann damit fahren. Daher gemeinsame Basis:
class Kraftfahrzeug { void start(); void fahren(); }; class LKW : public Kraftfahrzeug { //...LKW Zeugs }; class Motorrad : public Kraftfahrzeug { //...Motorrad Zeugs }; class Auto : public Kraftfahrzeug { //...Auto Zeugs }; void benutzeKFZ(Kraftfahrzeug & kfz) //soll beliebige KFZ-Typen benutzen { kfz.start(); kfz.fahren(); } LKW lkw; Motorrad motorrad; Auto auto; //jaja... benutzeKFZ(lkw); //okay, lkw *ist* ein KFZ benutzeKFZ(mottorrad); //okay, mottorrad *ist* ein KFZ benutzeKFZ(auto); //okay, auto ist *ist* KFZSchlechtes Beispiel:
Im mathematischen Sinne ist Kreis zwar eine Spezialisierung von Ellipse, im onjektorientierten Sinne nicht:
class Ellipse { void setX(); void setY(); }; class Kreis : public Ellipse { //irgendwie wird hier setY entfernt }; void benutzeEllipse(Ellipse & e) //soll beliebige Ellipsentypen benutzen { e.setX(x) e.setY(y) } Ellipse e; Kreis k; benutzeEllipse(e); //okay benutzeEllipse(k); //boing... Kreis ist *keine* Ellipse, weil kein setY
-
Die Ellipse hat eine Methode die der Kreis nicht braucht, deswegen
ist der Kreis objektorientiert gesehen keine Ellipse.
-
blurry333 schrieb:
Die Ellipse hat eine Methode die der Kreis nicht braucht, deswegen
ist der Kreis objektorientiert gesehen keine Ellipse.Nicht nur, dass er sie nicht braucht, sie ist einfach FALSCH. Man kann mit einem Kreis nicht das gleich machen, was man mit einer Ellipse machen kann. Man kann die beiden also nicht beliebig austauschen.
-
blurry333 schrieb:
Die Ellipse hat eine Methode die der Kreis nicht braucht, deswegen
ist der Kreis objektorientiert gesehen keine Ellipse.Nein, die Ellipse hat Eigenschaften die ein Kreis nicht hat (und daher auch nicht bekommen darf). Wie das umgangen wird sei dahin gestellt. Allerdings kann der Kreis nicht in allen Fällen als Ellipse eingesetzt werden.
Es könnte ja sein das der Entwickler von Ellipse setX und setY überschreibt:class Ellipse { float x, y; public: virtual void setX(float value) { x = value; } virtual void setY(float value) { y = value; } float getArea() const { return ... } }; class Kreis : public Ellipse { public: void setX(float value) { Ellipse::setX(value); Ellipse::setY(value); } void setY(float value) { Ellipse::setX(value); Ellipse::setY(value); } };Ein anderer Autor will jetzt die Methode foo(Ellipse&) verwenden und übergibt hier eine Instanz von Kreis (weil Kreis ist ja eine Ellipse):
Kreis bar; foo(bar);void foo(Ellipse& e) { e.setX(20); e.setY(10); assert(e.getArea() == 157.079f); }Der Autor von foo trifft allerdings eine Annahme, nämlich das die zurückgelieferte Fläche der einer Ellipse mit den achsen 10 und 20 entspricht. Für den Kreis wird allerdings 10^2*Pi geliefert, also nicht ganz den Erwartungen entsprechend...
-
Wenn man die Ellipse als gestreckten Kreis betrachtet, könnte man sie vom Kreis erben lassen:
struct Kreis { double radius; Kreis(double radius); virtual ~Kreis(); virtual double getArea() const { return radius * radius * PI; } }; struct Ellipse : public Kreis { double xstreckung; Ellipse(double radius, double xstreckung) : Kreis(radius) , xstreckung(xstreckung) { } virtual double getArea() const { return Kreis::getArea() * xstreckung; } };Natürlich ist eine Ellipse nicht im den Sinne ein Kreis, dass sie sich genauso verhält. Die Ellipse erweitert den Kreis um eine weitere Eigenschaft. Dabei kann sie sich aber auch genau wie ein Kreis verhalten, nämlich wenn (im Beispiel oben)
Ellipse::xstreckung1.0ist.
-
blurry333 schrieb:
Die Ellipse hat eine Methode die der Kreis nicht braucht, deswegen ist der Kreis objektorientiert gesehen keine Ellipse.
Ja, das ist des Rätsels Lösung.
Würde man diese Funktion bei Ellipse übrigens auch nicht brauchen, dürfte man wieder vererben. Man müßte sich aber klar sein, daß man sie auch in Zukunft nicht brauchen wird. Das bewußte Weglassen von Sachen, die die Außenweltbegriffe können, aber unsere Modellbegriffe nicht, ist ein wichtiger Teil der Softwareentwicklung und sollte nicht einfach aus Versehen passieren oder ignoriert werden. Die Modellbegriffe werden dann nach C++ übersetzt und Klassen genannt. Ignoriert man diese Modellierung, sondern versucht unvernünftigerweise die Außenwelt in Klassen einzufangen, darf man praktisch niemals eine Klasse von einer anderen erben lassen, weil es in der Außenwelt so viele gemeine Ausnahmen gibt, Säugetiere mit doch nicht sieben Halswirbeln, Säugetiere, die doch Eier legen, Automobile mit drei Rädern, Lehrer, die keinen Unterricht halten...
Und das wollen wir doch alle nicht, oder?
-
TyRoXx schrieb:
Wenn man die Ellipse als gestreckten Kreis betrachtet, könnte man sie vom Kreis erben lassen:
struct Kreis { double radius; Kreis(double radius); virtual ~Kreis(); virtual double getArea() const { return radius * radius * PI; } }; struct Ellipse : public Kreis { double xstreckung; Ellipse(double radius, double xstreckung) : Kreis(radius) , xstreckung(xstreckung) { } virtual double getArea() const { return Kreis::getArea() * xstreckung; } };Natürlich ist eine Ellipse nicht im den Sinne ein Kreis, dass sie sich genauso verhält. Die Ellipse erweitert den Kreis um eine weitere Eigenschaft. Dabei kann sie sich aber auch genau wie ein Kreis verhalten, nämlich wenn (im Beispiel oben)
Ellipse::xstreckung1.0ist.Ja, geht andersrum auch.
class Ellipse { float x, y; public: Ellipse(fload _x, float _y){ x=_x; y=_y; } float getArea() const { return PI*x*y; } }; class Kreis : public Ellipse { public: Kreis(floar r):Ellipse(r,r) //getArea() darf geerbt werden };Aber mit dem setX und setY macht man alles kaputt.