private vs. public Member



  • Was spricht dagegen z.B. bei einem Spiel die zentrale Klasse Game nicht mit öffentlichen Datenelementen zu versehen?

    Game.speed = 5

    vs.

    Game.set_speed(5)

    Nun hört man oft "da verändert man ja bestimmt öfter mal versehentlich die Werte der Datenelemente der Klasse".

    Dieses Argument - das auch das Einzige mir bekannte ist - finde ich aber relativ schwach, da aus einem Ausdruck wie

    Game.speed = Game.speed * 2

    Game.set_speed(game.get_speed() * 2)

    wird.

    Der 1. Ausdruck ist viel einfacher zu lesen und dementsprechend ist der Code auch einfacher zu pflegen.

    [img]images/smiles/icon_confused.gif[/img]

    /edit pumuckl: Titel für FAQ geändert



  • Hallo,
    stell dir vor, du willst speed auf ein bestimmtest Intervall beschränken. In so einem Fall mußt du nur eine Methode ändern, nämlich die set-Methode der Klasse. Wäre speed öffentlich, dann müßtest du deinen gesamten Code durchwühlen, alle Vorkommnisse von speed finden und prüfen, ob diese korrekt sind.
    Eine öffentliche Instanzvariable bricht immer das Prinzip der Kapselung. Das muss aber nicht grundsätzlich falsch sein. Es kommt nur darauf, was du willst. Willst du eine Kapselung, dann mach' keine öffentlichen Instanzvariablen. Ist dir Kapselung egal, dann mach was du willst images/smiles/icon_smile.gif



  • Für solche Fälle bietet es sich dann auch vielleicht an, eine Methode

    void Game::multiplySpeed(int multiplier)
    {
       speed *= multiplier;
    }
    

    einzuführen, falls solche Konstrukte häufiger vorkommen. Ich kann Hume nur zustimmen, diese public-Datenelement sind ein Hauptgrund, warum man bei Änderungen an bestehenden Programmen verzweifelt. Es ist nicht überschaubar, wo überall nachher zugegriffen wird, man muß das mühsam über "Suchen" lokalisieren und n-mal ändern, obwohl einmal gereicht hätte.

    Falls Du bemerkst, daß Du häufig solche set(get() + irgendwas) verwendest, dann solltest Du für diese Operationen wie von mir vorgeschlagen eigene Funktionen implementieren. Die häufige und mehrfache Verwendung von solchen Ausdrücken deutet nämlich darauf hin, daß es sich eigentlich um eine Eigenschaft der Klasse handelt, die Du aber hin zum Aufrufer verlagerst. Der Aufrufer sollte eigentlich gar nicht wissen müssen, daß er die Gamespeed durch eine Multiplikation erhöht. Er sagt dem Spiel einfach "schneller um Faktor 2" oder etwas in der Art, und die Klasse selbst weiß dann schon was sie machen muß.

    Ähnlich kann das z.B. für die Position der Spielfigur gelten, die ein Koordinatenpaar (x,y) besitzt. Für eine Bewegung sollte nicht unbedingt ein

    BadMonster.setX(BadMonster.getX() + 1);
    BadMonster.setY(BadMonster.getY() + 1);
    

    herauskommen. Besser ist dann, wenn es ein

    BadMonster.moveDeltaX(1);
    BadMonster.moveDeltaY(1);
    

    oder

    BadMonster.moveDeltaXY(1, 1);
    

    gibt. Den Idealzustand einer solchen Kapselung hast Du erreicht, wenn sich Dein eigentliches Hauptprogramm ganz locker flockig lesen läßt wie ein Kochrezept, also so:

    BadMonster.moveDeltaXY(2, -2);
    GoodAngel.moveDeltaXY(1, 1); // hey, die Monster sind schneller
    if (BadMonster.isCollision(GoodAngel))
    {
       GoodAngel.startAnimation(killed);
       Game.End();
    }
    

    Ok, das ist nur ein Beispiel für die Idee. Aber obiges Programm kannst Du sofort lesen und verstehen, ohne daß Du weißt wie isCollision funktioniert.

    Nimm das Gegenbeispiel (auch in C++), das so aussieht:

    BadMonster.set_X(BadMonster.get_X() + 2);
    BadMonster.set_X(BadMonster.get_X() - 2);
    GoodAngel.set_X(GoodAngel.get_X() + 1);
    GoodAngel.set_Y(GoodAngel.get_Y() + 1);
    if ( BadMonster.get_X() >= GoodAngel.get_X() &&
         (BadMonster.get_X() + BadMonster.get_Width()) ... 
        /* blubber, dazu bin ich jetzt zu faul mir das auszudenken */
    )
    {
       Animation anim(AnimationPool.GetAnimationKilledPtr());
       GoodAngel.loadAnimation(anim);
       while (anim.isRunning())
       {
          Sleep();
       }
       anim.unload();
       Game.setTitle("End of Game");
       Game.playMelodie("endgame.wav");
       Game.uninitializePlayer();
    }
    

    Ich denke mal Du erkennst die wesentliche Idee... das gilt übrigens für jedes Programm, egal ob Spiel oder sonstige Anwendung. Diese ganzen Details interessieren eigentlich auf der Aufruferebene nicht die Bohne.

    [ 29.09.2001: Beitrag editiert von: Marc++us ]



  • Danke, das hat mir schon sehr geholfen images/smiles/icon_smile.gif



  • Welcher Stil ist besser?

    game.get_fps()
    game.fps()



  • Hallo,
    das ist wohl reine Geschmacksache. Die STL verwendet eher den letzteren Stil. Es gibt aber auch genug Bibliotheken die die erste Variante verwenden. Mir persönlich gefällt GetFps() am Besten. Aber wie gesagt, es ist Geschmack- bzw. Gewöhnungssache. Hauptsache du machst es einheitlich.



  • Auf jeden Fall solltest Du das Get irgendwie einbauen!

    Also niemals

    klasse::fps();
    

    sondern immer

    klasse::get_fps();
    klasse::GetFps();
    klasse::getFps();
    

    Die Information steckt im "get", nicht darin ob man es groß oder klein schreibt.

    Umgekehrt gilt dies übrigens auch für "set"-Methoden.

    Weiterhin sollten die Methodennamen immer aus einem Verb und einem Begriff (dem Attribut, auf das sich die Veränderung bezieht) zusammengesetzt sein.

    Wenn durch eine komplexere Methode mehr als nur das Setzen von Werte bewirkt wird, sollte dies auch entsprechende hervorgehen:

    Klasse::calculateAlitude();
    Klasse::refreshTimingCycles();
    

    Abfragen auf bool-Zustände beginnt man häufig mit is, also so:

    bool Klasse::isNoMonstersLeft();
    bool Klasse::isGameStarted();
    

    Aber ob Du das nun Is, is_, is schreibst ist nicht so dramatisch... nur einmal auswählen und dann immer durchhalten.



  • Da kann ich Marc++us nur zustimmen !

    Ich benutze immer die Notation "GetSomething" bzw. "SetSomething" etc. und die private-Variablen werden bei mir dann klein geschrieben wie "int something".

    Wenn ich nun 2 Variablen "something" bräuchte, würde ich Sie "something_1" und "something_2" nennen, wobei die Zugriffsmethoden dann "GetSomething1" und "GetSomething2" heissen.

    Gibt es eigentlich für sowas nicht auch einen Standard? Ich habe mir zwar meine persönliche Notation angewöhnt, die finde ich auch sehr gut und kann oft Ähnlichkeiten zu anderen Programmierern feststellen. Aber viel besser wäre es doch, wenn es EINE Notation gäbe, die jeder C++ - Programmierer benutzt.
    Also, würde mich mal insteressieren, gibt es so einen Standard? Wenn nicht, könnten man ja in diesem Forum mal eine gemeinsame Notation ausarbeiten. Fänd ich cool !



  • Großbuchstaben bei Methodennamen finde ich unschön:

    game.GetCharacter()

    Nur Kleinbuchstaben sind aber meiner Meinung nach unleserlich

    game.getcharacter()

    Deshalb verwende ich den Unterstrich zwischen Verb und Nomen

    game.get_character()

    P.S. es gibt bestimmt einige Leute im Netz, die da schon gute Modelle entwickelt haben!



  • Bei Methoden schreibe ich das erste Wort (das Verb) klein, den Rest groß: game.getCharacter()



  • Hallo,
    einen Standard gibt es wohl nicht. Allerdings basieren alle Namen der Javaklassenbibliothek auf dem Schema, das ansatzweise von Marc++us beschrieben wurde. Also zusammengesetzte Namen (keine Unterstriche). Erster Buchstabe des ersten Worts klein. Alle anderen ersten Buchstaben groß.
    Die STL hingegen verwendet fast gar keine get-, set-Methoden (also zumindest nicht im Namen images/smiles/icon_smile.gif Eine Methode die in Java z.B. getAt heißen würde, heißt hier nur at. Es gibt auch keine is-Methoden (wie z.B. isEmpty).



  • ich muß mich manchmal zusammenreißen, statt isEmpty nicht emptyp zu schreiben images/smiles/icon_biggrin.gif



  • Wie sieht euer Stil bei Kommentaren/Gliederung aus?
    Ich will meinen nämlich verbessern images/smiles/icon_biggrin.gif

    //===================================================================
    //==    displays bitmaps
    //===================================================================
    
    void Game::ShowBMP(char *image, 
                 char *background, 
                 int x, 
                 int y, 
                 SDL_Surface *window, 
                 unsigned flag1, 
                 Uint32 color, 
                 unsigned flag2, 
                 Uint8 alpha)  
    {
        //============================================================
        //== define needed surfaces and rects
        //== static avoids defining-deleting-circle
        //============================================================
    
        static SDL_Surface *bitmap = SDL_LoadBMP(image);
        static SDL_Surface *tmp = SDL_LoadBMP(background);
        static SDL_Surface *fps_surface;
    

    SO gliedere ich Funktionen / Methoden und kleine Einheiten, die bestimmte Aufgaben erfüllen.
    Allerdings kann man so schwer erkennen, ob der Teil der bei

    //=====
    //== ...
    //=====
    

    steht nun eine Funktion / Methode ist oder nur ein Teil einer Funktion / Methode images/smiles/icon_sad.gif .



  • Zunächst mal sollten Kommentare nicht lästig sein... ich persönlich finde das mit solchen Trennern //********************** etc wie Du es auch zeigst im Code eher störend als praktisch.

    Bei mir sieht das im Code eher so aus:

    // im Todesfall alles abbauen, und falls keine Leben mehr
    // Spiel beenden
    if (player.isDead())
       playMelodie();
    if (player.getLives() == 0)
       game.End();
    
    // und nun der nächste Block, davor eine Leerzeile!
    

    Ich denke nämlich, daß Leerzeilen ebenso gut strukturieren, aber nicht so stören wie diese /**********. Vor allem hat man den Code ohnehin coloriert am Schirm, d.h. es kommt eine Leerzeile und danach z.B. grüner Text, also Kommentar. Das gliedert schon sehr ordentlich.

    Viel wichtiger als die Optik ist bei Kommentaren der Inhalt! Falls die Funktionsnamen so sprechend sind wie hier:

    if (player.isDead())
       game.playDeathMelody();
       game.end();
    

    Was soll da noch als Kommentar kommen? Ein Kommentar "falls Spieler tot, eine Melodie spielen und Spiel beenden" ist totaler Nonsense, das kann man ja auch so direkt erkennen. Also bei solchen Dingen lieber den Codeblock mit einer (oder mehr) Leerzeilen abtrennen vom Rest, und sich einen dümmlichen Kommentar ersparen. Höhepunkt diesbezüglich sind solche Kommentare:

    a += 5; // erhöhe a um 5
    

    woraus dann nach einigen Updates

    a += 6; // erhöhe a um 5
    

    wird. Sowas ist nun wirklich Scheiße, kann man nicht anders sagen.

    Für Funktionsköpfe würde ich was völlig Neues nehmen, ich überlege schon ob wir das nicht bei uns in der Firma einführen werden.

    Und zwar kommt das mit dem neuen Visual Studio .net, obwohl für C# gemacht, wird das auch in C++ funktionieren. Und zwar XML-Kommentare in den Funktionsköpfen. Ich poste mal ein Beispiel:

    class XmlElement

    {

    /// <summary>

    /// Returns the attribute with the given name and

    /// namespace</summary>

    /// <param name="name">

    /// The name of the attribute</param>

    /// <param name="ns">

    /// The namespace of the attribute, or null if

    /// the attribute has no namespace</param>

    /// <return>

    /// The attribute value, or null if the attribute

    /// does not exist</return>

    /// <seealso cref="GetAttr(string,string)"/>

    public string GetAttr(string name, string ns)

    {

      return "test";
    

    }

    }[/cpp]

    Es gibt nämlich dann Reportgeneratoren, die aus diesem Kommentar HTML-Files erstellen können (sogar mit Querverweisen als Link, dazu dienen die cref-Tags). Das halte ich für eine sehr gute Idee, das gab's ja auch bisher schon mit javadoc usw., aber diese Methode wird sich zumindest im Dunstkreis des Visual Studios auf alle Fälle etablieren und es ist auch mit einer größeren Zahl an Reportgeneratoren zu rechnen, wäre also überlegenswert.



  • Hallo,
    da ich Doxygen für die Erstellung von HTML-Übersichten benutze, sehen meine Kommentare in etwa so aus:

    /** 
     * @class CBankDocument
     * @brief Diese Klasse ist die Basisklasse der Dokumenthierarchie   
     * @author Benjamin Kaufmann Mat.Nr.: xxxxxxx
     * @date 21.07.2001 geändert: 21.07.2001
     */
    class CBankDocument : public CObject
    {
        private:
            CString m_Author;           /**< Speichert den Autor des Dokuments. */
            CTime m_DateOfFoundation;   /**< Speichert das Erstellungsdatum des Dokuments. */
            CString m_Title;            /**< Speichert den Titel des Dokuments. */
            CString m_Key;              /**< Enthält einen Schlüssel für den Schlüsselvergleich */
    
        public:
            /** @name Erzeugung und Vernichtung
             * Diese Methoden dienen der Erzeugung und Vernichtung von CBankDocument-Objekten.
             */
             //@{
            /**
             * Der Standardkonstruktor.
             * Erstellt ein CBankDocument-Objekt und initialisiert
             * die Instanzvariablen mit Standardwerten.
             * @post Ein neues CBankDocument-Objekt wurde erstellt.
             */
            CBankDocument( );
    
            /**
             * Der Initialisierungskonstruktor der Klasse.
             * Erstellt ein neues Objekt und initialisiert
             * die Instanzvariablen mit den übergebenen Werten.
             * @param aut Ein CString der den Namen des Autors enthält
             * @param tit Ein CString der den Titel des Dokuments enthält.
             * @param key Ein CString der den Schlüssel des Dokuments enthält
             * @post Ein neues CBankDocument-Objekt wurde erstellt. 
             *       Author, Titel und Key enthalten die übergebenen Werte.
             */
            CBankDocument(const CString& aut, const CString& tit, const CString& key);
    //...
    

    Innerhalb eine Methode kommentiere ich nur mit //. Und zwar kurz und knapp.



  • Hey HumeSikkins, ich verwende auch Doxygen images/smiles/icon_smile.gif
    Jedoch dokumentiere ich alles im *.cc-File (außer vielleicht der Klassenbeschreibung), damit der Klassenrumpf sauber bleibt und man alle Methoden sofort erhaschen kann images/smiles/icon_wink.gif
    Ich gehe dabei davon aus, dass diese Informationen nicht der braucht, der das Headerfile anschaut (der bekommt bestenfalls kurze Kommentare), sondern der Anwender/Progger - und der wird dann wohl eher zu Opera etc. greifen - zumindest ich tue es so images/smiles/icon_smile.gif

    bye



  • ist die documentation zu doxygen gut?
    ich mein sind die informationen, wie man am besten kommentiert, damit das programm das gu erkennt leicht zu finden? das programm selber ist ja schon extrem seltsam... die ganzen bezeichnungen:

    BLABLA_LALA

    und wenn die nicht so einfach zu finden sind, könnte mir dann jemand ne kurze auflistung der wichtigsten "Kommentar-befehle" machen? wäre echt net images/smiles/icon_smile.gif


Anmelden zum Antworten