Virtuelle Methode + const Referenz
-
Das spricht dann aber eher für
class Object { virtual Matrix const& getWorldMatrix()=0; }; class Animation { // getter oder setter für Matrizen, je nachdem ob die Animationen // einzelne Subklassen sind oder ob es eine Matrix gibt, // die über den setter konfiguriert werden soll };Such auch mal nach "non-virtual interface". Bringt zwar nix für die Lösung deines Problems, wenn du aber grad erst am gestalten bist, kannst du das ja umsetzen.
-
Nappl schrieb:
Nein, aufteilen kommt nicht in Frage. Ist totaler Designoverkill.
Nappl schrieb:
Was ich mir hole teste ich also mit einem if(obj->isAnimated())
Das würde ich wiederum als unausgereift ansehen.
Nappl schrieb:
Und das Array fuer den Fall, dass ich nur 1 Matrix brauche zu missbrauchen, mag ich auch nicht.
Wieso Mißbrauch? Ist doch sehr elegant, so gibt es überhaupt keine Fallunterscheidung.
Da du sowieso Referenzen auf bereits existierende Objekte zurückgibst, könnte man das Problem auch einfach durch ein Zeigerpaar (bzw. ein Rangeobjekt, oder ein Paar aus Zeiger und Längenangabe) erschlagen.
NVI wäre auch eine Möglichkeit, wobei dann immer noch die Frage zu stellen wäre, wie die private virtuelle Funktion auszusehen hat.
-
Wie wär's mit Visitor?
-
camper schrieb:
Nappl schrieb:
Nein, aufteilen kommt nicht in Frage. Ist totaler Designoverkill.
Nappl schrieb:
Was ich mir hole teste ich also mit einem if(obj->isAnimated())
Das würde ich wiederum als unausgereift ansehen.
Nappl schrieb:
Und das Array fuer den Fall, dass ich nur 1 Matrix brauche zu missbrauchen, mag ich auch nicht.
Wieso Mißbrauch? Ist doch sehr elegant, so gibt es überhaupt keine Fallunterscheidung.
Da du sowieso Referenzen auf bereits existierende Objekte zurückgibst, könnte man das Problem auch einfach durch ein Zeigerpaar (bzw. ein Rangeobjekt, oder ein Paar aus Zeiger und Längenangabe) erschlagen.
NVI wäre auch eine Möglichkeit, wobei dann immer noch die Frage zu stellen wäre, wie die private virtuelle Funktion auszusehen hat.
Ich brauche die Fallunterscheidung, weil sich eben ein animiertes Objekt deutlich von nicht animierten unterscheidet (logisch, oder?)
Ich kann nicht ins Detail gehen, das wuerde hier ausufern. Aber wenn das Ding nicht animiert ist, wird von der Engine automatisch die Matrix in ein GPU Register kopiert und im Falle eines animierten Meshs werden die Matrixen per ConstantTable gesetzt. Wie gesagt, es gibt eine Fallunterscheidung.Mir ging es auch eher um das Problem, dass ich nicht einfach schreiben kann
getWorldMatrices { vector(): } (Pseudocode), weil ich ja keine lok. Variable per Referenz liefern kann.
Vielleicht mach ich die Rueckgabewerte einfach als out Parameter statt return values.
-
Wenn sich zwei Typen so deutlich von einander unterscheiden, sollten sie halt nicht über die gleiche Schnittstelle angesprochen werden. Ich verstehe nicht was daran Overkill ist.
-
brotbernd schrieb:
Wenn sich zwei Typen so deutlich von einander unterscheiden, sollten sie halt nicht über die gleiche Schnittstelle angesprochen werden. Ich verstehe nicht was daran Overkill ist.
Weil ich es einfach sinnfrei halte, eine eigene Klasse einzufuegen, nur wegen EINER Methode. Ich finde das if() auch absolut nicht unelegant. Manche sind halt animiert und manche nicht.
Egal ob animiert oder nicht, letztlich brauch ich sie beide in einer Struktur. Wenn ich jetzt 2 Klassen haette (dieses HasOneMatrix etc.), welchen Typ von Objekten sollte ich dann in der Struktur speichern? Und wie sollte ich dann die Fallunterscheidung vornehmen?
-
Nappl schrieb:
Egal ob animiert oder nicht, letztlich brauch ich sie beide in einer Struktur. Wenn ich jetzt 2 Klassen haette (dieses HasOneMatrix etc.), welchen Typ von Objekten sollte ich dann in der Struktur speichern? Und wie sollte ich dann die Fallunterscheidung vornehmen?
Normalerweise schmeißt man nicht zusammen, was auch nicht zusammen gehört. Vererbung wird schon viel zu viel missbraucht.
Ignoriere uns wenn es dir nicht passt, ein schlechtes Design bleibt es dennoch (Und wie die folgen von so etwas aussehen, erlebe ich zu häufig in der Praxis - auch wenn niemand gänzlich davor gefeilt ist).
-
Nappl schrieb:
brotbernd schrieb:
Wenn sich zwei Typen so deutlich von einander unterscheiden, sollten sie halt nicht über die gleiche Schnittstelle angesprochen werden. Ich verstehe nicht was daran Overkill ist.
Weil ich es einfach sinnfrei halte, eine eigene Klasse einzufuegen, nur wegen EINER Methode. Ich finde das if() auch absolut nicht unelegant. Manche sind halt animiert und manche nicht.
Egal ob animiert oder nicht, letztlich brauch ich sie beide in einer Struktur. Wenn ich jetzt 2 Klassen haette (dieses HasOneMatrix etc.), welchen Typ von Objekten sollte ich dann in der Struktur speichern? Und wie sollte ich dann die Fallunterscheidung vornehmen?Ich weiß ja nicht genau was Du da machst, aber Bsp:
void DrawAnimated(AnimatedObject*); void DrawStatic(StaticObject*); Container<AnimatedObject*> animatedObjects_; Container<StaticObject*> staticObjects_; void DrawObjects() { ForEach(animatedObjects_,DrawAnimated); ForEach(staticObjects_, DrawStatic); } // statt: void DrawObject(Object* o) { if (o->IsAnimated()) DrawAnimated(o); else DrawStatic(o); } void DrawObjects() { ForEach(objects_, DrawObject); }Was bringt es verschiedene Objekte erst zusammenzuschmeißen und sie danach wieder mühsam auseinander zufummeln?
-
asc schrieb:
Nappl schrieb:
Egal ob animiert oder nicht, letztlich brauch ich sie beide in einer Struktur. Wenn ich jetzt 2 Klassen haette (dieses HasOneMatrix etc.), welchen Typ von Objekten sollte ich dann in der Struktur speichern? Und wie sollte ich dann die Fallunterscheidung vornehmen?
Normalerweise schmeißt man nicht zusammen, was auch nicht zusammen gehört. Vererbung wird schon viel zu viel missbraucht.
Ignoriere uns wenn es dir nicht passt, ein schlechtes Design bleibt es dennoch (Und wie die folgen von so etwas aussehen, erlebe ich zu häufig in der Praxis - auch wenn niemand gänzlich davor gefeilt ist).
Wegen jedem Scheissdreck eine eigene Klasse IST schlechtes Design. Genau das fuehrt zu Ueberdesign und tiefen Vererbungshierarchien.
Aber ist mal wieder typisch. Man kennt den ganzen Kontext NULL, aber gleich kommen die Klugscheisser angekrochen, die ja eh alles besser und immer perfekt implementieren und meckern rum.
Die Objekte unterscheiden sich noch in deutlich mehr Attributen, soll ich jetzt fuer jeden Dreck eine eigene Basisklasse machen? Der Vorschlag mit der eigenen Basisklasse fuer die 2 Funktionen ist absoluter Schwachsinn. Macht es Sinn zu sagen "Ein Objekt IST ein HasOneMatrix". Natuerlich nicht. Macht es Sinn zu sagen: Ein Objekt KANN animiert sein: Natuerlich. In diesem Forum gibts einfach zu viele klugscheisserische Theoretiker.
-
@brotbernd: Was es bringt sie in einer Struktur zu halten? Das kannst du nicht beurteilen, da du das System nicht kennst. Aber jeder der Ahnung von Engines hat weiss, dass aus Sicht des Culling Subsystems animierte und nicht animierte gleich behandelt werden koennen.
Und ich wuerde ja gerne mal wissen, was EIN if() mit "auseinander fummeln" zu tun hat. Jetzt sind wohl schon einzelne ifs verpoent bei den Design Gurus...
-
Wegen jedem Scheissdreck eine eigene Klasse IST schlechtes Design. Genau das fuehrt zu Ueberdesign und tiefen Vererbungshierarchien.
Wegen groben Unterschieden trotzdem alles in eine Klasse zu packen führt zu Gott-Klassen - ist das besser?
Nappl schrieb:
Macht es Sinn zu sagen: Ein Objekt KANN animiert sein: Natuerlich.
Macht es Sinn, einer Klasse eine Methode zu verpassen, die nur dann sinnvoll ist, wenn das Objekt einen speziellen state hat (anmiated) und in allen anderen Fällen absolut überflüssig oder sogar gefährlich ist? - Nein!
Hast du meinen Vorschlag oben gesehen, die Animation in eine eigene Klasse auszulagern? So könnte man alles, was mit animierten Objekten zu tun hat, in eine spezielle Klasse auslagern, ohne das Object-Interface aufzublähen.
-
Nappl schrieb:
Wegen jedem Scheissdreck eine eigene Klasse IST schlechtes Design.
Merkwürdig, ich dachte es geht hier um eine Basisklasse. Das heißt, die dient (vermutlich) vielen Klassen als Basis, was wiederum bedeutet das bezogen auf die Gesamtzahl die Trennung hier mit Sicherheit nicht die Anzahl prozentual viel erhöht.
Nappl schrieb:
Genau das fuehrt zu Ueberdesign und tiefen Vererbungshierarchien.
Ich trenne unterschiedliche Aspekte immer, und merkwürdigerweise habe ich nie mit tiefen Vererbungshierarchien zu kämpfen. Tiefe Vererbungshierarchien sind meist auch eher ein Zeichen von übermäßiger Verwendung von Vererbung an stellen an denen Komposition oder andere Mechanismen wesentlich geeigneter und flexibler wären.
Nappl schrieb:
Man kennt den ganzen Kontext NULL, aber gleich kommen die Klugscheisser angekrochen, die ja eh alles besser und immer perfekt implementieren und meckern rum.
Dein Beispiel ist aber schon ein Widerspruch zu dem, was Vererbung bedeuten soll. Vererbung heißt "IST EIN" und nicht "Ist entweder dies oder jenes". Wie willst du unterschiedliche Typen sinnvoll auseinander halten, ohne gleich noch Typinformationen in die Klassen zu packen (und damit - du magst ja kein Überdesign - unnötige Informationen mit zuführen).
Nappl schrieb:
Macht es Sinn zu sagen "Ein Objekt IST ein HasOneMatrix". Natuerlich nicht. Macht es Sinn zu sagen: Ein Objekt KANN animiert sein:
Eigentlich macht es einen Sinn wenn ein Objekt eindeutig etwas ist, und nicht nach der Variante "Och jo, könnte vielleicht sowas sein". Im Endeffekt würde die zusätzliche Basisklasse mit hoher Wahrscheinlichkeit weniger Codezeilen erfordern, als die Differenzierung wenn man Gottklassen baut (Und wesentlich wartbarer sein).
Genau deine Argumentation kenne ich auch aus einem ehemaligen Projekt... Merkwürdigerweise war mein Code im Vergleich trotz einem mehr an Klassen übersichtlicher und vor allem fehlerfreier und wartbarer. Und ebenso habe ich sehr flache Vererbungshierarchien (trotz vielen Klassen; mehr als 3 Ebenen ist für mich in der Regel ein Zeichen von schlechten, schwer wartbaren Code), die in sich auch übersichtlich und logisch verständlich sind.
-
Nappl schrieb:
Wegen jedem Scheissdreck eine eigene Klasse IST schlechtes Design. Genau das fuehrt zu Ueberdesign und tiefen Vererbungshierarchien.
Die Hierarchie wird nicht tiefer. Das ist auch kein Überdesign. Für den Leser wird hingegen am Interface direkt ersichtlich, ob ein Objekt animierbar ist oder nicht. Bei Dir wird das erst zur Laufzeit klar. Und ein nichtanimierbares Objekt ist kein animierbaren Objekt, und umgekehrt offensichtlich auch nicht. Ergo: Ein einziges Interface ist scheisse.
-
Nappl schrieb:
Aber ist mal wieder typisch. Man kennt den ganzen Kontext NULL, aber gleich kommen die Klugscheisser angekrochen, die ja eh alles besser und immer perfekt implementieren und meckern rum.
Hier wird das Liskov Prinzip für Vererbung verletzt. Das ist quasi ein Garant dafür dass das Design schlecht ist.
Deshalb, wenn man in einem Forum wie diesem hier fragt, wo unglaublich gute Leute unterwegs sind, dann ist auf diese Leute zu hören nicht unbedingt die dümmste Idee.
-
Nappl schrieb:
Weil ich es einfach sinnfrei halte, eine eigene Klasse einzufuegen, nur wegen EINER Methode. Ich finde das if() auch absolut nicht unelegant. Manche sind halt animiert und manche nicht.
Aber selbstverständlich führt man wegen einer unterschiedlichen Methode verschiedene Klassen ein. Das ist doch der Sinn von Vererbung - die Basisklasse garantiert ein Interface, das jede abgeleitete Klasse erfüllen muss. Wenn das nicht alle beherrschen, kann der Client-Code ein Objekt der Basisklasse nicht ohne genaues Wissen um die Vererbungshierarche benutzen, und damit hast du den objektorientierten Super-GAU. Genau aus diesem Grund ist der if-Hack auch ausgesprochen widerlich.
Egal ob animiert oder nicht, letztlich brauch ich sie beide in einer Struktur. Wenn ich jetzt 2 Klassen haette (dieses HasOneMatrix etc.), welchen Typ von Objekten sollte ich dann in der Struktur speichern? Und wie sollte ich dann die Fallunterscheidung vornehmen?
Denk dir eine Schnittstelle in der Basisklasse aus, die die Funktonalität bereitstellt, die der Client-Code braucht. Dafür ist die Basisklasse da. WxSkip hat ein Wort in die Runde geworfen, das hier angebracht sein könnte (Visitor); denkbar beispielsweise
struct Drawable { virtual void draw(Drawer &) = 0; }; struct StaticDrawable : Drawable { virtual void draw(Drawer &d) { d.draw(*this); } }; struct AnimatedDrawable : Drawable { virtual void draw(Drawer &d) { d.draw(*this); } };...wo dann Drawer zwei verschiedene Methoden draw(StaticDrawable&) und draw(AnimatedDrawable&) zur Verfügung stellt. Ich bin mir im Übrigen bewusst, dass "drawer" im Englischen auch "Schublade" bedeutet, die Nomenklatur würde ich in der Form also nicht 1:1 übernehmen. Um Drawer nicht zu einer Gottklasse verkommen zu lassen, könnte es allerdings sinnvoll sein, die Zeichenmethode direkt in StaticDrawable und AnimatedDrawable zu implementieren oder per CRTP einen Verweis auf freie Funktionen zusammenzustricken.
Es gibt natürlich eine ganze Reihe anderer Möglichkeiten, an das Problem heranzugehen. Wenn der Anwendungsfall es hergibt, würde ich über eine Strategy-Implementation nachdenken - das wäre dann schön modular. Ich denke da etwa an
class Drawable { public: typedef std::function<void(DeviceContext &, Drawable&)> drawing_strategy_t; void draw(DeviceContext &dc) { drawing_strategy_(dc, *this); } void set_drawing_strategy(drawing_strategy_t const &strategy) { drawing_strategy_ = strategy; } private: // Default-Strategy im Konstruktor setzen oder so drawing_strategy_t drawing_strategy_; };
-
Nappl schrieb:
Aber ist mal wieder typisch. Man kennt den ganzen Kontext NULL, aber gleich kommen die Klugscheisser angekrochen, die ja eh alles besser und immer perfekt implementieren und meckern rum.
Ist mal wieder typisch. Man stellt ne Frage wie man etwas "genau so" machen kann. Wenn die Antwort einfach "kann man nicht" heißt und "was du erreichen willst kann man aber anders erreichen", wird man ausfallend und beleidigend.
Wenn du so schlau bist, dass du das einzig wahre Design für dein Problem gefunden hast, dann bist du sicher auch schlau genug, das Design einfach umzusetzen. Deine Frage zeigt, dass das wohl nicht so einfach ist, weil die Sprache das nicht hergibt. Was asc und andere dir sagen wollen ist, dass dein Design wohl angepasst werden muss. Dein Gezeter legt aber eher nahe, dass du dir wohl besser eine Sprache suchst, die zu deinem Design passt. Viel Erfolg.
-
Nappl schrieb:
Aber ist mal wieder typisch. Man kennt den ganzen Kontext NULL, aber gleich kommen die Klugscheisser angekrochen, die ja eh alles besser und immer perfekt implementieren und meckern rum.
Das hast du richtig erkannt. Wir kennen nur das bischen Information was wir von Dir haben und bemühen uns trotzdem eine sinnvolle Antwort zu geben. Allerdings bist Du der einzige der hier meckert und zwar nicht zu knapp. Freiwilliger Helfer als Klugscheißer zu beschmipfen empfinde ich sogar als extrem unverschämt. Was erwartetst Du, wenn du nach einem Verbesserungsvorschlag fragst? Eine Zustimmung zu deinem Ansatz? Dass dieser schlecht ist, hast Du doch schon längst selber erkannt, denn Du stößt auf das Problem mit dem Du Dich an uns gewendet hast: Konkrete Typen der Object Hierarchie haben entweder die eine oder die andere Schnittstelle. Niemals beide. DAS ist das Problem, wenn versucht wird unterschiedliche Konzepte in eines zu quetschen.
Nappl schrieb:
@brotbernd: Was es bringt sie in einer Struktur zu halten? Das kannst du nicht beurteilen, da du das System nicht kennst. Aber jeder der Ahnung von Engines hat weiss, dass aus Sicht des Culling Subsystems animierte und nicht animierte gleich behandelt werden koennen.
Klar kenn ich das System nicht. Aber das kannst Du nicht mir zum Vorwurf machen. Wenn, dann ich Dir.
Ich hab zwar keine Ahnung von "Culling Subsytems", aber ich sehe trotzdem kein Problem. Dass animierte und nicht animierte Objekte bezüglich des Cullings gleich behandelt werden können ändert doch nichts daran, dass ich die Schnittstellen bezüglich anderer Algorithmen auftrennen muss (interface segregation). AnimatedObject und StaticObject können sich ja durchaus eine Schnittstelle für das Culling System teilen, da spricht doch nichts gegen. Aber im Zugriff auf ihre Matrix/Matrizen unterscheiden sie sich halt.