Klasse Coord



  • Die Parameter der eben genannten Funktionen müssen auch nicht const sein. Da hier sowieso kopiert wird macht das keinen Sinn.

    Coord::Coord (int newx, int newy)
        : xc( newx )
        , yc( newy )
    {}
    void Coord::x (int newx)
    {
       xc = newx;
    }
    
    void Coord::y (int newy)
    {
       yc = newy;
    }
    

    Ich persönlich finde die Name der Setter nicht gut gewählt. Ich würde das etwas prägnanter ausdrücken wie z.Bsp. mit setX() oder so. Das ist aber Ansichtssache.



  • Werner Salomon schrieb:

    Der Einsatz solcher Setter-Methoden ist gerade bei einer Koordinatenklasse zweifelhaft. Ich würde Dir empfehlen, es erst einmal ohne zu versuchen. Es verführt ggf. zu einem ungünstigen Programmierstil.
    Das ist zumindest meine ganz persönliche Meinung.

    Verstehe ich jetzt nicht. Wenn ich die Koordinate nur in einer Dimension verschieben möchte, muss ich doch nicht unbedingt die andere kennen? Und wenn ich sie kennen muss, benötige ich ja wieder eine Methode für den Zugriff. Das läuft für mich auf ein Henne-Ei-Problem hinaus. Außer ich führe zum Beispiel eine Verschiebe-Methode ein, wo ich dann mit einem Delta arbeiten kann. Das löst aber nicht das Problem, dass externe Funktionen meist die Werte einzeln benötigen und ich damit wieder zumindest bei den Gettern gelandet bin.



  • Ich denke, Werner meinte eher, dass die Variablen öffentlich sein sollten und dass ganz auf Getter und Setter verzichtet würde. Falls es sich um eine Vektorklasse handelt – was durch den operator+ naheliegt, aber Coord als Namen disqualifiziert – kann ich dem nur zustimmen.



  • Nexus schrieb:

    Ich denke, Werner meinte eher, dass die Variablen öffentlich sein sollten und dass ganz auf Getter und Setter verzichtet würde.

    Hallo Nexus,

    nein das meinte ich nicht; ganz im Gegenteil. Ich meine, dass bei einem OO-Ansatz, wie es Coord gegenüber dem öffentlichen x-y-Positionen darstellt, die Kapselung der Internas eine wichtige Eigenschaft ist; und Settermethoden brechen die Kapselung auf.
    Ich hatte zum identischen Thema vor drei Jahren eine Diskussion mit Jester, die aber für meinen Geschmack recht fruchtlos endete.

    Nick Unbekannt schrieb:

    Wenn ich die Koordinate nur in einer Dimension verschieben möchte, muss ich doch nicht unbedingt die andere kennen?

    Hallo Nick,

    das ist genau der Punkt. Ein SetX veschiebt kein X, sondern setzt es. Eine Verschiebung einer Koordinate (oder eines Vektors; ich will da an dieser Stelle gar keinen Unterschied machen) geschieht doch idealerweise mit:

    Coord delta = ..;
        Coord pos;
        pos = pos + delta;
    

    oder? Und es gibt keinen Grund warum delta nicht gleich Coord(0,1) oder Coord(1,0) sein kann. Aber jetzt lasst bitte die Performancekeule stecken 😉 .

    Es hat mir bis heute niemand einen (für mich!) vernünftigen Grund nennen können, warum u.a. SetX(-Y) bei einer Koordinate sinnvoll sein soll. Versucht es doch mal, ich würd' das gerne diskutieren.

    Ansonsten hatte ich schon darauf hingewiesen:

    Werner Salomon schrieb:

    Das ist zumindest meine ganz persönliche Meinung.

    Gruß
    Werner



  • Werner Salomon schrieb:

    Hallo Nexus,

    nein das meinte ich nicht; ganz im Gegenteil. Ich meine, dass bei einem OO-Ansatz, wie es Coord gegenüber dem öffentlichen x-y-Positionen darstellt, die Kapselung der Internas eine wichtige Eigenschaft ist; und Settermethoden brechen die Kapselung auf.

    Generell handhabe ich das ähnlich wie du, aber im Bezug auf Vektorklassen habe ich persönlich noch nie einen Mehrwert von grosser Kapselung gesehen. Im Gegenzug war ich schon oft froh, direkt auf die Member zugreifen zu können.

    Werner Salomon schrieb:

    Es hat mir bis heute niemand einen (für mich!) vernünftigen Grund nennen können, warum u.a. SetX(-Y) bei einer Koordinate sinnvoll sein soll. Versucht es doch mal, ich würd' das gerne diskutieren.

    Ich weiss nicht, inwiefern du das Beispiel als vernünftig erachtest, aber ich habe bei einem Jump'n'Run-Spiel einmal eine Kollisionsabfrage bei Tiles (Bausteine) mit 45°-Ebene implementiert.

    +-------+
    |     /#|
    |   /###|
    | /#####|
    +-------+
    

    Das rechte untere Dreieck kollidiert mit dem Spieler, das obere linke ist "Luft". Jedenfalls fragte ich die X-Koordinate der Spielfigur ab. Dadurch konnte ich die Y-Position neu berechnen (da es sich um eine 45°-Gerade handelt, war das nicht schwierig) und abhängig von der X-Position setzen, damit die Spielfigur sich immer entlang der Diagonale bewegte.

    Ist halt etwas sehr spezifisch, vielleicht fällt mir noch was Besseres ein.



  • Nexus schrieb:

    .. ich habe bei einem Jump'n'Run-Spiel einmal eine Kollisionsabfrage bei Tiles (Bausteine) mit 45°-Ebene implementiert.

    +-------+
    |     /#|
    |   /###|
    | /#####|
    +-------+
    

    Das rechte untere Dreieck kollidiert mit dem Spieler, das obere linke ist "Luft". Jedenfalls fragte ich die X-Koordinate der Spielfigur ab. Dadurch konnte ich die Y-Position neu berechnen (da es sich um eine 45°-Gerade handelt, war das nicht schwierig) und abhängig von der X-Position setzen, damit die Spielfigur sich immer entlang der Diagonale bewegte.

    Ist halt etwas sehr spezifisch, vielleicht fällt mir noch was Besseres ein.

    Das ist ein schönes Beispiel.
    Sobald man das Problem etwas verallgemeinert, sieht man, dass ausschließlich Operationen auf Koordinaten (Vektoren) nötig sind. Mal angenommen, der Spieler bewegt sich auf der 'Oberfläche'. Dann ist der Pfad, auf dem er sich bewegt, vorgegeben - z.B. durch einen Polygonzug. Wenn man es 'realistisch' simulieren will, ist u.a. seine Geschwindigkeit über x natürlich nicht konstant, sondern sie ist z.B. konstant bezogen auf den zurückgelegten Weg. Also es gibt ein Delta pro Zeiteinheit, welches in der Länge konstant ist; eine reine Vektoroperation.

    Anderer Fall: angenommen, die Spielfigur springt auf die Oberfläche. Dann gibt es einen Schnittpunkt zwischen der 'Sprungkurve' (eine Parabel oder auch eine senkrechte Gerade(Fall)) und dem Polygonzug der Oberfläche. Das Ergebnis ist eine Koordinate. Gerade im Fall einer Senkrechten (ein Fall oder Sprung) tut man sich für eine einigermaßen realistische Simulation keinen Gefallen, das ganze als y=f(x) darzustellen, sondern hier ist ein Coord=f(t) (über die Zeit) angebracht.

    Ich bin mir dessen bewusst, dass man insbesondere bei integer-Koordinaten einiges 'vereinfachen' kann. Wenn meine Jump&Run-Umgebung nur aus horizontalen und 45°-Segmenten besteht, so spricht nichts dagegen, es so zu machen, wie Du es angedeutet hast. Aber ich meine, man sollte sich bewußt sein, dass das Programm dann auch auf dem Level stehen bleibt. Zumal ein allgemeinerer Entwurf in der Praxis gar nicht so viel aufwendiger - also teurer - ist.

    Gruß
    Werner



  • Hallo Werner,

    den Punkt mit dem Delta hatte ich ja schon akzeptiert. Durch den Constructor ist ja ein impliziter Setter gegeben, der eben nur als ganzes Objekt fungiert. Aber wie löst du das, wenn du die Koordinate an jemanden übergeben willst, der mit der Klasse nichts anfangen kann? Schreibst du dann eine friend-Funktion als Koordinator?



  • Natürlich kann man das Beispiel so weit verkomplizieren und abstrahieren, dass es auch mit Vektoren geht. Das war aber keine physikalische Simulation mit parametrisierten Kurven, sondern ein 2D-Spiel. Glaubst du im Ernst, die Spielfiguren folgen nach dem Springen einer parabolischen Kurve? 😉

    Dass Vektoren prinzipiell keinen Komponentenzugriff benötigen, zeigt bereits die folgende Überlegung: Du kannst die einzelnen Komponenten jeweils durch das Skalarprodukt mit Einheitsvektoren extrahieren. Ebenso kannst du Multiplikation von Einheitsvektoren mit Skalaren und Vektoraddition dazu verwenden, beliebige Vektoren zusammenzubasteln. Ganz ohne Get und Set, nur die mathematischen Operationen müssen halt Zugriff haben.

    Naja, für Mathematiker mag das zwar eleganter sein, ich habs jedoch gerne naheliegend. Ausserdem ist es schneller.



  • Nick Unbekannt schrieb:

    .. wie löst du das, wenn du die Koordinate an jemanden übergeben willst, der mit der Klasse nichts anfangen kann? Schreibst du dann eine friend-Funktion als Koordinator?

    Du sprichst einen neuralgischen Punkt an. Nun - ich rufe getX und getY - gegen die Getter hatte ich in diesem Threads noch nichts gesagt; obwohl ich ihnen durchaus skeptisch gegenüber stehe. Aber genau für diesem Zweck braucht man sie.

    Gruß
    Werner



  • Werner Salomon schrieb:

    Aber genau für diesem Zweck braucht man sie.

    Nicht, wenn man Skalarprodukt hat (siehe oben) 🙂

    Anderes Beispiel: Eine Kugel mit (x,y)-Geschwindigkeitsvektor prallt an einer vertikalen Wand ab. Ich würde einfach x = -x; setzen. Wie machst du das?



  • Nexus schrieb:

    Natürlich kann man das Beispiel so weit verkomplizieren ..

    ich habe die Erfahrung gemacht, dass das gar nicht komplizierter ist

    Nexus schrieb:

    Glaubst du im Ernst, die Spielfiguren folgen nach dem Springen einer parabolischen Kurve? 😉

    warum nicht! 😉

    Es ist völlig ok, wenn Du das 2D-Spiel so angehst, wie beschrieben. Ich habe nur die Erfahrung gemacht (und das ist meine Erfahrung), dass solche Setter zu einem unsauberen Programmierstil verführen, der die Kapselung bricht und bei größeren Projekten schnell zu unpflegbaren Programmen führt. Ich empfehle jedem, es einfach ohne Setter zu versuchen; oder es zumindest sparsam einzusetzen.
    Was ich schlimm finde, ist dieser Automatismus: da ist ein Attribut und schon wird eine Getter- und Setter-Methode geschrieben. Das ist mit Sicherheit die falsche Vorgehensweise. Es gibt genug Tools, die OO im Namen führen, die genau das machen.

    Nexus schrieb:

    Ausserdem ist es schneller.

    wie oben schon erbeten:

    Werner Salomon schrieb:

    Aber jetzt lasst bitte die Performancekeule stecken 😉 .

    Gruß
    Werner



  • Nexus schrieb:

    Anderes Beispiel: Eine Kugel mit (x,y)-Geschwindigkeitsvektor prallt an einer vertikalen Wand ab. Ich würde einfach x = -x; setzen. Wie machst du das?

    Na ja - mit dem vorher gesagten sollte das doch klar sein. Wieso sind alle Wände immer vertikal? das ist doch wieder eine Einschränkung der Allgemeinheit - die hier auch nichts bringt. Die Wand hätte in meinem Modell einen Normalenvektor und an dem wird reflektiert. Das passt dann für alle anderen Wände auch. Hier spare ich die Fallunterscheidung.



  • Werner Salomon schrieb:

    ich habe die Erfahrung gemacht, dass das gar nicht komplizierter ist

    Also ich persönlich finde eine Kurve mit Parametrisierung der Zeit und Schnittpunktberechnung schon ein bisschen komplizierter als ein y = x .

    Werner Salomon schrieb:

    warum nicht! 😉

    Wäre doch schade, wenn man sich in der Luft nicht mehr bewegen könnte und bei Sprüngen aus grosser Höhe so schnell würde, dass die Spielfigur unkontrollierbar wird. Aber wir scheinen uns im Bezug auf das 2D-Spiel ja einig zu sein. 🙂

    Werner Salomon schrieb:

    Ich habe nur die Erfahrung gemacht (und das ist meine Erfahrung), dass solche Setter zu einem unsauberen Programmierstil verführen, der die Kapselung bricht und bei größeren Projekten schnell zu unpflegbaren Programmen führt. Ich empfehle jedem, es einfach ohne Setter zu versuchen; oder es zumindest sparsam einzusetzen.

    Du sagst es, weg mit den Settern! Genau deshalb habe ich ja öffentlichen Zugriff auf die Variablen! 😃

    Scherz beiseite: Ich achte auch darauf, Methoden nur anzubieten, wenn sie sinnvoll sind. Aber nur solange es auch vernünftige und intuitive Alternativen gibt. Ansonsten machen gut gewählte Methoden den Code verständlicher und einfacher. Teilweise sind gewisse Methoden prinzipiell redundant, aber doch von Nutzen. Anderes Beispiel:

    object.SetPosition(object.GetPosition() + offset);
    // vs.
    object.Move(offset);
    

    Werner Salomon schrieb:

    Wieso sind alle Wände immer vertikal? das ist doch wieder eine Einschränkung der Allgemeinheit - die hier auch nichts bringt.

    Sie bringt Einfachheit. Warum kann es nicht Situationen geben, die nur vertikale Wände erfordern? Man muss nicht immer alles so allgemeingültig und erweiterbar wie möglich halten. Du schreibst ja auch nicht jede Klasse als Template, nur weil sich in ferner Zukunft mal ein Typ ändern könnte.

    Natürlich hast du völlig Recht, wenn sich die Tendenz abzeichnet, dass mehr Wände kommen. Aber präventive, kategorische Generizität halte ich nicht immer für eine gute Idee.



  • Ich würde nur SetPosition() anbieten. Weil Move() könnte als SetPosition() missinterpretiert werden. Oder wenn es sein muss dann ganz klar MoveRel() und MoveAbs() . Kommt aber auch auf die Häufigkeit der Verwendung an.



  • Nick Unbekannt schrieb:

    Ich würde nur SetPosition() anbieten. Weil Move() könnte als SetPosition() missinterpretiert werden.

    "To move" heisst bewegen/verschieben. Meiner Meinung nach ist relative Bewegung naheliegender, gerade wenn es noch zusätzlich ein SetPosition() gibt. Und sonst Doku lesen 😉

    MoveRel() und MoveAbs() gefällt mir hingegen nicht.


Anmelden zum Antworten