Vererbung: Rechteck und Quadtrat!?
-
Hey,
In einem Thread (denn ich nich merh finde) wurde mal diskutiert ob man ein Rechteck von einem Quadrat ableiten würde oder andersrum...
Da gabs doch verschieden Vorzüge...
grüße
-
und was genau willst du jetzt wissen?
-
Weder, noch - ich würde beides von einer abstakten Klasse "geometrisches Objekt" ableiten und notfalls eine Umwandlung von Quadrat nach Rechteck liefern.
-
hmm.. ob man quadrat von rechteck ableitet, oder ob man das rechteck vom quadrat ableitet..
Ich war der meinung das ich das quadrat vom rechteck ableite, udn so beide seiten höhe/breite gleich setze.. aber diese ansatz wurde kritisch diskutiert...
-
Genau das selbe ob man Pinguine von Vögeln ableiten sollte, da Pinguine Vögel sind, dennoch aber die Grundeigenschaft des Vogels, das Fliegen nicht beehrschen. Ähnliche Lösung wie bei CSToll:
class Bird { }; class FlyingBird : publich Bird { public: virtual void fly() = 0; }; class NonFylingBird : public Bird { }; class Pinguin : public NonFlyingBird; class Taube : public FlyingBird;
Effetiv C++. Glaube sogar, dass das mit den Rechtecken ne Einführung dazu war.
Edit: Effektiv nicht Exceptional
-
Allgemein betrachtet bedeutet Vererbung ja immer eine "ist-ein" Beziehung. z.B. ein Auto "ist-ein" Fahrzeug -> ich leite Auto von Fahrzeug ab. Ein Quadrat wiederum ist ein Rechteck, aber nicht andersrum. Also kann man ein Quadrat von einem Rachteck ableiten, aber andersrum ists unsinnig. Wie sinnig das jetzt im jeweiligen Kontext ist, sei mal dahingestellt. Wenn du mehr geometrische Formen verwaltest, muesste das Quadrat auch vom Trapez und von der Raute ableiten, da wirds dann schon komplizierter.
-
Pinguin "ist ein" Vogel, trozt der fehlenden eigenschaft "fliegen":)
Die reale Welt ist wohl nich 100% portierbar in Objekt/Klassen strukturen...
-
BorisDieKlinge schrieb:
P
Die reale Welt ist wohl nich 100% portierbar in Objekt/Klassen strukturen...Exakt
-
Na ja, das ist eben der Ansatz, dass bei der Objektorientierung immer eine Erweiterung vorliegen sollte und keine Einschränkung.
Ein Quadrat definiert sich aber eben halt über eine Kantenlänge und hat entsprechend seine Attribute Funktionen SetLength beispielsweise, dagegen hat ein Rechteck eben SetWidth, SetHeight => du kannst Länge und Breite unabhängig voneinander verändern. Wenn ein Quadrat ein Rechteck ist (im Sinne, es erbt von Rechteck), dann müsste das eben auch für das Quadrat zutreffen (dass die Länge und Breite voneinander unabhängig verändert werden können), tut es aber nicht, denn dann wäre es kein Quadrat mehr.
Also löse das Problem wie CStoll und KasF
-
@pumuckl: Du mußt auch unterscheiden zwischen "mathematischem ist-ein" und "ist-ein" aus OOP-Sicht. Letzteres bedeutet, daß du mit einem Objekt der abgeleiteten Klasse alles machen kannst, was die Basisklasse auch kann:
(das Beispiel hatte ich auch im Flags-Thread gepostet)
void resize(rect& r) { r.setWidth(5); r.setHeight(10); ASSERT(r.Width()==5 && r.Height()==10);//Invariante des Rechtecks - Höhe und Breite sind unabhängig voneinander } int main() { square q; resize(q); ASSERT(q.Width()==q.Height());//Invariante des Quadrats - Höhe und Breite sind gleich }
(beide Assertions kontrollieren nur, daß die Invarianten der Klassen erfüllt bleiben - aber egal wie du die Klassen aufbaust, fliegt dir eine von beiden um die Ohren)
-
Oder die Beziehung der Objekte in der Realenwelt ist nicht immer korrekt, da:
Man hätte den Pinguin nich in die Kategorie Vogel stetzen sollen, da man ja im vornerein wusste das er nich fliegen kann
-
KasF schrieb:
BorisDieKlinge schrieb:
P
Die reale Welt ist wohl nich 100% portierbar in Objekt/Klassen strukturen...Exakt
bzw. die "ist-ein" Beziehung hat einfach eine andere Bedeutung^^
-
BorisDieKlinge schrieb:
Pinguin "ist ein" Vogel, trozt der fehlenden Eigenschaft "fliegen":)
Was uns zu dem Schluss bringt, dass "fliegen" keine allgemeine Eigenschaft von Voegeln ist sondern nur von einer Unterklasse, den FlyingBirds, zu denen Pinguine nunmal nicht gehoeren (wie oben aber auch schon gesagt wurde).
Was das Quadrat/Rechteck Problem angeht: Die Eigenschaft, dass beim Rechteck die Seiten unabhaengig voneinander sind, sind keine Verfeinerung, sondern eine Verallgemeinerung. Genauso wie die Eigenschaft, dass bei einem Fahrzeug im Allgemeinen die Zahl der Raeder nicht spezifiziert ist, bei einem Motorrad aber eben immer 2 ist.
Die Erweiterung, die das Quadrat gegenueber dem Rechteck hat, ist die angesprochene Invariante, dass beide Kantenlaengen gleich sind. Die Settermethoden (so sie denn sinnvoll sind) muessten natuerlich diese Invariante garantieren, so dasssetlength()
undsetHeight()
jeweils beide Kantenlaengen veraendern muessten, wie man es eben erwartet, wenn ein Quadrat ein Quadrat bleiben soll.
CStollsASSERT(r.Width()==5 && r.Height()==10);
bringt auch nicht die "Invariante" der unabhaengigen Seitenlaengen zum Ausdruck sondern verlangt konstante Seitenlaengen. Invarianten muessen, wie der Ausdruck schon sagt, immer erfuellt bleiben. Das obige
ASSERT
verhindert einzig die Aenderung der Kantenlaengen.Sprich: beim Nachmodellieren der wirklichen Welt muss man darauf achten, was ein Name wirklich bedeutet, und nicht was man sich landlaeufig darunter vorstellt. Ein Vogel ist nunmal zu allererst definiert durch seinen Biologischen Hintergrund, nicht durch die Eigenschaft, dass er ein flugfaehiges Wesen ist.
-
Ist zwar mit Ellipse und Kreis, kommt aber auf das gleiche heraus.
http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.6Persönlich halte ich es für das beste zwei unabhängige Klassen zu definieren und eine implizite Konvertierung Quadrat => Rechteck und eine explizite anders herum welche auch fehl schlagen kann.
Ein Rechteck kann man nicht immer als Quadrat ansehen daher ist ein Quadrat keine Basis für ein Rechteck. Ein Quadrat kann man immer als Rechteck ansehen jedoch sobald man anfängt ihn als Rechteck zu verändern ist es kein Quadrat mehr. Man verändert also den Type und kommt deswegen nicht ohne Konvertierung aus. Sobald man ein Quadrat als Rechteck betrachtet wird es zum Rechteck.
Man könnte jedoch const Quadrat von const Rechteck ableiten. Davon würde ich aber auch im allgemein Fall abraten da die Konvertierung hier billiger wird als jede Vererbung.
OT: Zum Thema Vögel die nicht fliegen können http://www.metacafe.com/watch/297785/kiwi/
-
pumuckl schrieb:
CStolls
ASSERT(r.Width()==5 && r.Height()==10);
bringt auch nicht die "Invariante" der unabhaengigen Seitenlaengen zum Ausdruck sondern verlangt konstante Seitenlaengen. Invarianten muessen, wie der Ausdruck schon sagt, immer erfuellt bleiben. Das obige
ASSERT
verhindert einzig die Aenderung der Kantenlaengen.Was erwartest du denn, wenn du einem Rechteck die Seitenlängen 5 und 10 gibst? Die Invariante des Rechtecks ist, daß eine gesetzte Kantenlänge nicht durch irgendwelche unabhängigen Aufrufe verändert werden sollte (d.h. solange ich kein weiteres SetWidth() aufrufe, sollte die Breite auf dem gesetzten Wert bleiben).
-
CStoll schrieb:
Die Invariante des Rechtecks ist, daß eine gesetzte Kantenlänge nicht durch irgendwelche unabhängigen Aufrufe verändert werden sollte
JFTR, das ist keine Invariante.
-
Bashar schrieb:
CStoll schrieb:
Die Invariante des Rechtecks ist, daß eine gesetzte Kantenlänge nicht durch irgendwelche unabhängigen Aufrufe verändert werden sollte
JFTR, das ist keine Invariante.
Dann anders: wenn ich die Breite gesetzt habe und unmittelbar danach überprüfe, steht noch der von mir gesetzte Wert drin - andernfalls würde zumindest ich die Rechteck-Klasse in die Tonne treten (daß die Breite nach einigen Aufrufen von Hilfsfunktionen nicht mehr den ursprünglichen Wert haben muß, ist mir auch klar - aber ein SetHeight() hat absolut nichts mit der Breite zu tun).
-
wikipedia schrieb:
Das liskovsche Substitutionsprinzip ist ein Kriterium in der objektorientierten Programmierung, das angibt, wann ein Datentyp als Unterklasse eines anderen Typs modelliert werden soll. Es wird von vielen als das einzig gültige Kriterium angesehen
http://de.wikipedia.org/wiki/Liskovsches_Substitutionsprinzip
-
Bashar schrieb:
CStoll schrieb:
Die Invariante des Rechtecks ist, daß eine gesetzte Kantenlänge nicht durch irgendwelche unabhängigen Aufrufe verändert werden sollte
JFTR, das ist keine Invariante.
Bei allen "Just for the Records" und Spitzfindigkeiten sollte man imo nicht das Wesentliche (LSP, DBC) vergessen und das drückt CStolls Beitrag doch sehr gut aus.
Die Klasse Quadrat hat eine Invariante, nämlich height == width. Gleichzeitig sollte *jede* Methode über eine Postcondition verfügen. Die einzig sinnvolle Postcondition für setWidth/setHeight ist, dass die Breite/Höhe nach dem Aufruf dem Wert des Arguments entspricht (von mir aus mit Precondition: arg > 0). Das wiederum ist aber unvereinbar mit der Invarianten eines Quadrats.
Wenn man also Quadrat von Rechteck ableiten will, dann darf man entweder keine getrennten Funktionen für setHeight/setWidth bereitstellen oder man muss völlig künztliche Pre-/Postconditions definieren. Mache ich das nicht, dann kann Clientcode (und nur um den geht es bei öffentlicher Vererbung) nicht mehr typunabhängig implementiert werden.
-
Was Wikipedia unter obigem Link zum Thema schrieb:
Zu beachten ist hierbei, dass die Entscheidung jeweils abhängig vom konkreten Fall ist. Ist beispielsweise eine Manipulation der geometrischen Figur nach der Erzeugung nicht vorgesehen, so kann Kreis durchaus von Ellipse abgeleitet sein: Dann ist "die Achsen können unabhängig voneinander skaliert werden" keine Eigenschaft der Klasse Ellipse, und somit muss sie auch keine Eigenschaft von Kreis sein, um Kreis zur Unterklasse von Ellipse zu machen.
Damit waere dann auch schon wieder ne Menge gesagt.
Zugegeben, mit HumeSikkins Argumenten macht es nicht unbedingt Sinn, ein Quadrat von einerm Rechteck abzuleiten, wenn denn das Rechteck die Settermethoden deklariert. Sollte sowas noetig sein, dann kann man es wieder wie mit dem Pinguin und dem Vogel halten: Man bilde eine Klasse
mathRect
, leite davonmathSquare
undconcreteRect
ab sowieconcreteSquare
vonmathSquare
. Wobei die beiden Konkreten Klassen die Settermethoden etc. beinhalten, die math* Klassen jedoch nur den mathematischen Aspekt beinhalten, naemlich dass ein Rechteck ein Viereck mit 4 rechten Winkeln und 2 Kantenlaengen ist, und ein Quadrat wiederum ein Rechteck mit 2 gleichen Kantenlaengen ist.
Ob die konkreten Klassen dann von den mathematischen ableiten oder diese nur beinhalten ist wieder ein anderes Thema.
Ich bleibe jedoch dabei, dass man, wenn man nur die mathematischen Aspekte betrachtet, ein Quadrat durchaus von einem Rechteck ableiten kann. (Getter und Setter sind keine mathematischen Aspekte)