Strukturierung von Klassen



  • Ja ne, Du hast Recht. Das sollte ich noch ändern. Hatte ich gar nicht mehr richtig auf dem Schirm.

    Wichtig war mir die Trennung von Art des Objekt, die Verwaltung, und die Berechnungen. Zwar habe ich immer noch eine Klasse, die äußerlich alles beinhaltet, aber sie beherbergt nur die Daten und führt sie zur Berechnung weiter.

    Wen ich Operation und Ausgabe an einem Objekt haben möchte, kann ich mir das nicht anders vorstellen?


  • Mod

    @lemon03 sagte in Strukturierung von Klassen:

    Wen ich Operation und Ausgabe an einem Objekt haben möchte, kann ich mir das nicht anders vorstellen?

    Ostream ist doch auch eine Klasse, die alles ausgeben kann, ohne mit dem, was sie ausgibt, etwas zu tun zu haben.



  • @lemon03 sagte in Strukturierung von Klassen:

    Wen ich Operation und Ausgabe an einem Objekt haben möchte, kann ich mir das nicht anders vorstellen?

    Spricht aus meiner Sicht nix dagegen. Ich habe auch öfter Klassen, die Daten halten und verändern und zusätzlich noch eine oder mehrere Ausgabemöglichkeiten haben:

    • saveToFile( ... )
    • toString()
    • ostream-Operator
      Je nachdem was man braucht.


  • @seppj sagte in Strukturierung von Klassen:

    @lemon03 sagte in Strukturierung von Klassen:

    Wen ich Operation und Ausgabe an einem Objekt haben möchte, kann ich mir das nicht anders vorstellen?

    Ostream ist doch auch eine Klasse, die alles ausgeben kann, ohne mit dem, was sie ausgibt, etwas zu tun zu haben.

    Und GraphObject ist doch eine Klasse, die mit dem was die ausgibt, nichts zu tun hat? Sie hat nur einen vector<Point>, den sie weiterreicht und ausgibt.



  • Und wohin ausgibt? Direkt mittels cout?

    Und was mir noch aufgefallen ist:

    • bei deinen (erstgezeigten) Klassen fehlen die public:-Bereiche. Oder sollen diese nur über die friend-Klassen ansprechbar sein (also lesend und schreibend)?
    • du hast doch die Klasse SideLength (wenn mich auch der Name irritiert)? Warum hast du in deinen Klassen (besonders PPAttributes) dann noch einzelne _x, _y, _z -Member?
    • den Aufbau (bzw. die Schnittstelle) deiner beiden Klassen PointProjectionund AffineTransformation finde ich auch eigenartig (keinen Konstruktor, Rückgabewert der Funktionen - auf jeweiligen Member points ???).


  • Zwar in die Konsole, aber rein über eine WinAPI-Funktion. Die ist aber ganz unten, das sie im restlichen Code nicht mehr auftaucht. Die beiden Ausgaben schauen so aus

    void GraphObject::draw( const DrawArea& draw,
                            const int x, const int y,
                            const Char& chr,
                            const int char_mix_mode,
                            const bool show ) const
    {
        if ( wired )
        {
            if ( edges.size() > 0 )
            {
                for ( std::size_t i = 0; i < edges.size(); ++i )
                {
                    const std::size_t a = edges[ i ].a;
                    const std::size_t b = edges[ i ].b;
                    drawLine( draw,
                              points[ a ].cx +x, points[ a ].cy +y,
                              points[ b ].cx +x, points[ b ].cy +y,
                              chr, char_mix_mode );
                }
            }
            else
            {
                drawDot( draw, points.front().cx +x, points.front().cy +y, chr, char_mix_mode, show );
            }
        }
        else
        {
            std::cerr << "GraphObject::draw(): wired warning\n";
            std::cerr << "no WireFrame";
            ready();
        }
    }
    
    void GraphObject::plot( const DrawArea& draw,
                            const int x, const int y,
                            const int char_mix_mode ) const
    {
        for ( const auto& point : points )
        {
            drawDot( draw, point.cx +x, point.cy +y, point.chr, char_mix_mode );
        }
    }
    
    • Ja, die Attribute-Klassen ( vielleicht hätte ich eher private-structs nehmen sollen) sollen vollkommen private sein und nur für die Friend-Klasssen nutzbar.

    • Habe ich das? Das muss dann ein Fehler sein. Die Seitenlängen sind wie die Namen nur zur besseren Übersicht beim printen eines Objekt. Die kommen in den Berechnungen nicht vor. Zwar sind die bei WireFrame alle gleich, aber bei Shape sind es zwei verschiedene. Da die Objekte dreidimensional dargestellt werden, habe ich auch drei benannt.
      Jedenfalls haben die mit den Attribute-Klassen rein gar nichts zu tun.

    • Ja, das war mit besten Wissen und Gewissen. Ich habe da lange überlegt, wie ich das hinbekomme. Irgendwann hat sich dies als die beste Lösung rauskristallisiert.
      Ich hatte bestimmt auch eine Konstruktor-Übergabe überlegt, das es die nicht gibt, war wahrscheinlich das es damit nicht wie gewünscht lief.



  • Ich meinte eher die print()-Funktionen in den einzelnen Klassen, oder was machen diese?

    Ok, dann ist SideLengthnur zufällig genauso eine Datenstruktur, um drei Dimensionen zu speichern.
    Und bei PPAttributesmeinte ich daher so etwas:

    struct Vector // or whatever name you like
    {
         double x, y, z;
    };
    
    struct PPAttributes
    {
        Vector move;
        // ...
        Vector scale;
    };
    

    Und wie benutzt du dann die beiden Klassen PointProjection und AffineTransformation? Greifst du (z.B. beim Initialisieren oder beim Auslesen) direkt auf points zu?

    Das ist alles andere als gutes OOP.



  • Die print-Funktionen "printen" die Daten als reine Zahlenwerte

    void GraphObject::print( const int amount ) const
    {
        console.setCursorHome();
        std::cout << name << "  side lengths:\n";
        auto n = 8;
        const auto np = 3;
        std::cout << "\n x: " << std::setw( n ) << std::fixed << std::setprecision( np ) << side_length.x;
        std::cout << "  y: "  << std::setw( n ) << std::fixed << std::setprecision( np ) << side_length.y;
        std::cout << "  z: "  << std::setw( n ) << std::fixed << std::setprecision( np ) << side_length.z;
        pp.print();
        af.print();
    
        int l = 0;
        std::cout << "\nPoints:";
        for ( const auto& point : points )
        {
            std::cout << "\n x: " << std::setw( n ) << std::fixed << std::setprecision( np ) << point.x;
            std::cout << "  y: "  << std::setw( n ) << std::fixed << std::setprecision( np ) << point.y;
            std::cout << "  z: "  << std::setw( n ) << std::fixed << std::setprecision( np ) << point.z;
            ++l;
            if ( l > amount ) break;
        }
        l = 0;
        n = 4;
        std::cout << "\n\nMap:";
        for ( const auto& point : points )
        {
            std::cout << "\n  cx: " << std::setw( n ) << point.cx;
            std::cout << "  cy: "   << std::setw( n ) << point.cy;
            ++l;
            if ( l> amount ) break;
        }
        pressKey();
    }
    

    Was Du meinst, muss ich erst mal kurz überlegen, auf die Schnelle kann ich jetzt dazu nichts schreiben.

    Tja, und der letzte Punkt? Muss ich wahrscheinlich dann noch mal ransetzen.



  • Genau das meinte ich. Was wenn du dann die Daten in eine Datei schreiben wolltest?
    Dafür gibt es doch den Stream-Operator:

    std::ostream& operator <<(std::ostream& os, const GraphObject &graphObject);
    

    Übergebe einfach std::ostream& als Parameter an deine print-Funktionen und rufe dann diese im Stream-Operator auf.
    Natürlich passen dann console.setCursorHome() und pressKey() nicht mehr, aber diese gehören ja auch generell nicht in eine Logik-Klasse.



  • Aha, danke. Nachvollziehbar. Das schaue ich mir mal an.


Anmelden zum Antworten