Was ist für euch guter Programmcode?



  • Matthias Lange|Work schrieb:

    Gregor schrieb:

    - defensiver Programmierstil

    Was ist das? 😕

    Ich verseteh darunter ein gesundes Mißtrauen den anderen gegenüber, welches sich im Code widerspiegelt. Mit den anderen sind die Nutzer deines Codes gemeint, was auch du sein kannst. Man sollte den Code bespielsweise so schreiben, dass Fehler, die von anderen bei der Benutzung des Codes begangen werden schnell auffallen. Das heißt, dass man Parameter auf ihre Gültigkeit überprüft und nicht einfach nach irgendwohin weiterreicht oder mit ihnen rechnet: Wenn du in einem Setter die Gültigkeit des Parameters nicht überprüfst und die Membervariable einfach so auf den gegebenen Wert setzt, dann brauchst du dich nicht zu wundern, wenn irgendwo im Programm ein Fehler auftaucht, den du dir nicht erklären kannst. Defensiver Programmierstil beschränkt sich aber natürlich nicht auf Fehlerchecks, sondern kann auch andere Aspekte beinhalten. Beispielsweise kann es manchmal sinnvoll sein, Kopien von Parametern oder Rückgabewerten anzulegen, damit nicht plötzlich jemand unbeabsichtig außerhalb des Objekts dieses verändert. Weiterhin gehört ein sinnvolles Exception Handling natürlich auch zu einem defensiven Programmierstil. ...und so weiter. Grob gesagt heißt defensiver Programmierstil "so programmieren, dass möglichst wenig Bugs entstehen und, dass Bugs möglichst schnell erkannt und gefunden werden können".



  • Ja, ein defensiver Programmierstil ist wichtig. Da versuch ich auch drauf zu achten. Es erspart einem unmengen an Arbeit, wenn man Fehler entweder gleich vermeiden kann oder sie sich relativ schnell zur Wort melden.



  • @Download?: Nur weil nicht jeder den Code sehen darf, heißt es ja nicht, dass es niemand darf. Ich käme nie auf die Idee zu sagen: Hey, mein Code ist der beste.



  • Gregor schrieb:

    Beispielsweise kann es manchmal sinnvoll sein, Kopien von Parametern oder Rückgabewerten anzulegen, damit nicht plötzlich jemand unbeabsichtig außerhalb des Objekts dieses verändert.

    Erstmal danke für deine ausführliche Erklärung, aber in diesem Beispiel setze ich die Werte des Objekt doch besser Privat wenn sie nicht von außerhalb des Objekts verändert werden sollen.



  • Sascha2 schrieb:

    Erstmal danke für deine ausführliche Erklärung, aber in diesem Beispiel setze ich die Werte des Objekt doch besser Privat wenn sie nicht von außerhalb des Objekts verändert werden sollen.

    Hier mal ein Beispiel in Java. Ist so vielleicht nicht direkt auf c++ übertragbar, aber in ähnlicher Weise sicherlich.

    public class Calendar
    {
       private Date currentDate;
       //...
    
       public Date getCurrentDate()
       {
          return currentDate;
          /* Das ist hier möglicherweise schlecht, weil ein Nutzer der Klasse
             das aktuelle Datum möglicherweise nur lesen können soll, nicht 
             aber verändern können soll. Wenn er hier das zurückgegebene 
             Date-Objekt verändert, verändert er hierdurch das entsprechende
             Date-Objekt innerhalb des Calendar-Objekts, da diese identisch sind.
             Besser wäre hier, eine Kopie des Date-Objekts in der Art
             return new Date(currentDate);
             zurückzugeben oder das zurückgegebene Objekt in einem anderen Objekt 
             zu wrappen, welches den Zugriff auf einen rein lesenden beschränkt.
           */
       }
    }
    


  • wird in C++ automatisch gemacht, solange du nicht explizit eine Referenz oder einen Pointer zurueckgibst



  • Shade Of Mine schrieb:

    asdrubael schrieb:

    Das kommt halt drauf an ob man den sort nur benutzen möchte oder man davon getrieben wird genau zu verstehen was da überhaupt gemacht wird.

    Wie würdest du denn eine Funktion sort() kommentieren?
    (...)
    das WAS sollte IMHO aus dem code hervorgehen.

    Ich würde schreiben was da wie (nach welchen Kriterien) sortiert wird, ganz einfach. Ich hab leider keine Kristallkugel aus der hervorgeht wieviel Verständnis ein möglicher Leser meines Codes über die Sprache x, die Bibliothek y oder das OS z hat.



  • Gregor! In C++ returned er eine Kopie. Und eine Reference zurück geben, macht man nicht (auch wenn es möglich ist).



  • Artchi schrieb:

    Gregor! In C++ returned er eine Kopie. Und eine Reference zurück geben, macht man nicht (auch wenn es möglich ist).

    Wieso? Man kann doch eine const Reference zurück geben wenn der Benutzer nur lesen darf.



  • Artchi schrieb:

    Gregor! In C++ returned er eine Kopie. Und eine Reference zurück geben, macht man nicht (auch wenn es möglich ist).

    Wie baust du op=?

    MfG SideWinder



  • Prinzipiell stehen in meinen Quellcodes immer zuerst die
    Kommentare da, und erst danach schreibe ich den korres-
    pondierenden C++ Code nieder. Das sieht dann z.B. so aus:

    void MTactics::Computer_zieht(){
    
    	// Zuerst werden alle Zuege im hilfsvektorarray auf (-1,-1)
    	// gesetzt, um zu signalisieren, dass noch kein moeglicher
    	// Zug gefunden wurde
    	short i,j,k,m; // Allenfalls benoetigte Schleifenvariablen
    	short indextabelle[10];
    	UINT prinzmoe;
    	Vektor helper(-1,-1);
    	Vektor analysiert(0,0);
    	for(i=0;i<210;i++){
    		hilfsvektorarray[0][i]=helper;
    	}
    
    	m=0; // Diese Variable speichert den aktuellen Arrayindex im hilfsvektorarray
    
    	// Prinzipiell schaut das jetzt so aus:
    	// a) Klappere das Spielfeld von links oben nach rechts unten
    	//	  ab, falls du einen Stein entdeckst, der Dir gehoert DANN
    
    	for(i=0;i<10;i++){
    		for(j=0;j<10;j++){
    			if(Spielfeld[j][i].aktuell!=0){ // um nicht auf
    											// Nullzeiger zuzugreifen
    				if(Spielfeld[j][i].Hiesige_Steine[0]->besitzer==2){
    	// b) Frage die Anzahl der mit diesem Stein prinzipiell moeg-
    	//    lichen Zuege ab (im Zugvektorarray des Steins gespeichert)
    					prinzmoe=Spielfeld[j][i].Hiesige_Steine[0]->Zuegearray->GetZuegezahl();
    	// c) Fuer jeden prinzipiell moeglichen Zug rufe die Funktion
    	//	  ZugMoeglich(Zielfeld,Spielstein) auf
    					for(k=1;k<=prinzmoe;k++){
    						analysiert=Spielfeld[j][i].Hiesige_Steine[0]->Zuegearray->GetZug(k);
    						Startfeld.x=j; // Die Funktionen ZugMoeglich und ExecuteMove benoetigen
    						Startfeld.y=i; // ein gut initialisiertes Startfeld
    						if(ZugMoeglich(Startfeld+analysiert,Spielfeld[j][i].Hiesige_Steine[0])){
    						// d) Falls der Zug auch dann noch moeglich ist, landen die
    						//	  Koordinaten des Startfeldes (also die momentanen Werte
    						//	  der Schleifenvariablen) im hilfsvektorarray, ebenso
    						//	  die Koordinaten des Zielfeldes (Startfeld + gefundener
    						//	  moeglicher Zug)
    						//		NICHT VERGESSEN - c) und d) sind fuer jeden unter b)
    						//		gefundenen prinzipiell moeglichen Zug durchzufuehren
    						hilfsvektorarray[0][m]=Startfeld;
    						hilfsvektorarray[1][m]=Startfeld+analysiert;
    						m++; // bis zum naechsten gefundenen moeglichen Zug
    						}
    					}
    				}
    			}
    		}
    	}	// Schliessende Klammer der for-Schleife mit i
    
    	// e) Nach Durchlaufen der Schleifenkonstruktion a) bis d)
    	//	  befinden sich im hilfsvektorarray wahrscheinlich eine
    	//	  ganze Menge moeglicher Start und Zielfelder, wobei
    	//	  das erste Auftreten von (-1,-1) den ersten undurch-
    	//	  fuehrbaren Zugindex signalisiert
    	//	  uebrigens, die Variable m muesste ja jetzt eigentlich die Anzahl der
    	//	  gefundenen moeglichen Zuege enthalten!!! Falls 0, kam offenbar kein
    	//	  einziger moeglicher Zug zustande und wir koennen sofort zu Spielphase 7
    	//	  (Spielende) gehen!!!
    	if(m==0){
    		SendMessage(Startbutton,WM_SETTEXT,0,(LPARAM)"New");
    		MessageBox(NULL,"You win (Found no regular move for Computer)","Game over",0);
    		Spielphase=7;
    		return;
    	} // Andernfalls kommen wir jetzt schon zur Ausfuehrung des Zuges
    

    usw usf.

    Wie leicht zu sehen ist war ich hier gerade vor dem Problem, einem Programm beizubringen, wie es herausfindet, welche Züge es mit seinen Spielsteinen
    bei einem Brettspiel machen kann. Gute Kommentierung und über sich selber
    sprechende Bezeichner machen für mich einen gut leserlichen Quellcode aus.
    Der Vorteil ist einfach der, wenn man z.B. ein Programm für ein paar
    Wochen zur Seite legt und dann wieder versucht, es zu erweitern, hilft eine
    gute Kommentierung einem vor allem auch selber, sich im eigenen Programm aus-
    zukennen.
    Netten Abend noch 🙂



  • Gregors Beispiel ist super, auch direkt in C++ übertragbar, nur ist dort die Problematik nicht so groß:
    liefere nie eine Referenz auf interne Daten zurück
    heißt hier der Merksatz

    aber es steht ja generell dafür, dem User mehr zu erlauben als er eigentlich braucht. Dazu zählen auch set-Methoden die keine Werte checken, sondern blind den übergebenen Wert der internen Variablen zuweisen.

    Denn dann ist die interne Variable ja quasi public.
    Deshalb anderer Merksatz:
    erstelle getter/setter mit bedacht
    und
    setter die nix checken, sollten nochmal überdacht werden (ist wirklich jeder wert valid)

    letzter merksatz gilt für viele valuetypes nicht.



  • Mecnels schrieb:

    short i,j,k,m; // Allenfalls benoetigte Schleifenvariablen
    [...]
    m=0; // Diese Variable speichert den aktuellen Arrayindex im hilfsvektorarray
    [...]
    }    // Schliessende Klammer der for-Schleife mit i
    

    oha.. schreib die erklärungen mit a), b), c) usw oberhalb der funktion. den ganzen rest mit "diese variable beinhaltet dies, jene klammer macht das" lässt du weg. dann initialisierst du schleifenvariablen erst wenn du sie brauchst und schon hat dein code um einiges an elleganz gewonnen 😮 😋 👍

    edit: und was zum geier ist ein "hilfsvektorarray"? 😕



  • Wer findet den Code von Mecnels gut? 😮 😮



  • Ich auch nicht. Zu tief verschachtelt und zu wenig in sinnvolle Funktionen ausgelagert.



  • Was ich total schrecklich finde ist das er das SendMessage und MessageBox da rein programmiert hat.



  • Hi,

    ich finde Einheitlichkeit sehr wichtig wie z.B. hier:

    // Testet ob die Zahl gerade oder Ungerade ist.
    template<typename T> inline const bool isEven (const T& value)
    { return (!(value&1)); }
    
        // Eine Absolut Funktion.
    template<typename T> inline const T abs (const T& value)
    { return ((value < 0) ? -value : value); }
    
        // Rechnet value^2.
    template<typename T> inline const T square (const T& value)
    { return (value*value); }
    


  • Toooooollllllll



  • Du sagst toll, aber willste mal das Gegenstück zur Ordnung sehen?

    // Testet ob die Zahl gerade oder Ungerade ist.
    template<typename T> inline bool isEven(const T& value) { return !(value&1) ; }
    
    template<typename T> inline T abs (const T value)
    { 
        // Eine Absolut Funktion.
        return value < 0 ? -value : value; 
    }
    
    // Rechnet value^2.
    template<typename T> T square (T value){ 
        return (value*value); 
    }
    

    Ist doch Chaos, und vorallem bei diversen OpenSources sieht man so einen Müll.

    Für mich muss guter und ordentlicher Code einheitlich sein, erweiterbar sowie durchdacht und wiederverwertbar (also für die nächste 3-4 Jahre)



  • MaSTaH schrieb:

    Ich auch nicht. Zu tief verschachtelt und zu wenig in sinnvolle Funktionen ausgelagert.

    Was ist denn eine sinnvolle Funktion? Eine, mit der ich einzelne Zahlen in eine Klasse schreiben und abfragen kann? Oder eine Funktion, die bei ihrem Aufruf vollautomatisch einen KI gesteuerten Spielzug generiert? Eine Funktion des letzteren Typs muss natuerlich ein bisschen laenger sein, glaubst Du nicht? (Natuerlich operiert auch mein Programm auf der untersten Ebene mit so elementaren Setz und Lesefunktionen)

    Zu: Total schrecklich finde ich, dass er das MessageBox da hineinprogrammiert hat. Zum Glück des Spielers spielt das Programm stark genug, dass diese MessageBox ohnehin nie erscheint. (Nur ein Scherz, aber kannst es ja trotzdem gerne versuchen http://www.geocities.com/c_pruell/MecnelsTactics.zip).

    Vielleicht postet einfach jeder einen typischen, durchschnittlichen, kurzen Auszug aus seinen Programmen, und irgendwann küren wir dann den 'übersichtlichsten Code'. Na?

    Netten Abend noch.


Anmelden zum Antworten