Gedankenexperiment zu Code-Richtlinien
-
StellerFragen schrieb:
Gibts da einen Standard wie so ein Kommentar auszusehen hat? Was macht z.B. das Ausrufezeichen oder das \a?
Je nachdem welches Tool zum erstellen der Doku verwendet wird. Beliebt sind JavaDoc-Like Tools wie zB Doxygen.
-
StellerFragen schrieb:
Wie würde denn so ein Kommentar aussehen, der die Funktionalität einer Funktion beschreibt?
Es gibt ein paar quasi-Standards für solche Kommentare, da kann man dann z.B. doxygen oder ähnliche Programme auf den Code loslassen, um sich eine Dokumentation der Library zusammenstellen zu lassen.
Inhaltlich sollte der Kommentar ausführlich genug sein, damit ich weiß, wozu und wie ich diese Funktion einsetzen kann. Außerdem sollte das Format bei allen Kommentaren halbwegs einheitlich aufgebaut sein.
-
Ansonsten ist fast alles zu strikt und gleichzeitig nicht vollständig, da wird das Augenmerk fast überall auf die falschen Dinge gesetzt imo.
-
Also ich nehme auch an, Du meinst, dass das zusätzlich dazu kommt neben dem, was normalerweise da reinsoll. Ersteller finde ich super, Teststatus zweifelhaft (jemand ändert die Klasse und muss dann den Teststatus wieder löschen? Oder man hält die ganze Hierarchie dort vor; das würde ich irgendwie separat machen) Und
-
5-10 Zeilen find ich auch zu wenig. Wenn wir eine doppelte for-Schleife haben, und { und } jeweils in eine eigene Zeile schreiben, sind bereits 6 Zeilen weg. Jetzt darf ich in die unterste Ebene nur 4 Zeilen schreibe? Ich finde es in Ordnung zu sagen, eine Funktion/Methode sollte nur eine Seite umfassen (also vll. 40-50 Zeilen); 5-10 find ich viel zu wenig.
-
KA, was gegen rohe Pointer spricht. Wenn ich einen Container baue, der ausdrücklich nur Verweise auf nicht-verwaltete Objekte beinhalten soll und gleichzeitig klar ist, dass der Container abgebaut wird, bevor der verwaltende Container der Objekte abgebaut wird, ist das imo absolut in Ordnung. Auch andere Verweise brauchen oft keine Smartpointer, dann baue ich auch einfach keine ein.
-
Was ist so schlimm an globalen Variablen und Funktionen? Ich stecke immer alles in einen namespace, aber das gilt genau so für meine Klassen. Und da ich nicht jede Funktion in eine Klasse stecke, weil es oft keinen Sinn macht, ergeben sich zwangsläufig (namespace-)globale Funktionen. Das ist absoluter Standard imo.
-
Und wie willst Du das bewerkstelligen, wenn ein großes Team verschiedene Module bearbeitet? Sollen alle über jede einzelne Funktion ständig sprechen? Im Nachhinein ein paar Funktionen rauszustreichen erscheint mir da sinnvoller. Aber ob das so viel bringt...
-
Es gibt doch ein paar seltene Fälle, in denen goto angebracht ist, was spricht dagegen? Ich find ein Verbot davon nicht gut.
Noch was:
Ich glaube, die Codingrichtlinien muss man auch in Abhängigkeit davon abstellen, wie erfahren das Team ist und wie gut man es kennt. Wenn Du mit Anfängern arbeitest, sodass Du dazu sagen musst, dass goto fast immer schlechter Stil ist, dann musst Du noch eine breite Palette von weiteren Dingen klären...Wenn Du hingegen davon ausgehst, dass die meisten mitdenken und sich im C++-Umfeld bereits gut auskennen, brauchst Du eine ganze Menge der Restriktionen gar nicht erst.
-
-
Ich stimme generell Bashar zu. Über strenge Richtlinien ärgert sich das ganze Team, und sie führen zu kontraproduktiven Ausweichmanövern. Ich halte es für sinnvoller, zwischen Unterprojekten klare Schnittstellen (mit Unit-Tests!) zu definieren und die jeweils zuständigen selbst entscheiden zu lassen, was für ihren Teil am sinnvollsten ist - die wissen das im Zweifel eh am besten. Allenfalls kann es sinnvoll sein, Empfehlungen zu geben, und die können naturgemäß nicht sehr spezifisch sein.
Konkret zu den aufgezählten Punkten:
1.) Kommentare sind prinzipiell eine gute Sache, aber nur da, wo es auch etwas zu kommentieren gibt. Schnittstellen müssen natürlich gut dokumentiert sein (doxygen o.ä. empfiehlt sich), und wenn eine Backend-Funktion etwas macht, was nicht auf den ersten Blick als solches erkennbar ist, ist es geradezu fahrlässig, Kommentare zu unterlassen. Nichtsdestoweniger ist es ausgesprochen frustrierend, wenn Kommentare für jede Funktion, egal wie trivial, verlangt werden (insbesondere in Zusammenhang mit Punkten 2 und 7), und das wird zu massig copy&paste kurzer Codeteile führen. Auch musst du sehr vorsichtig sein, was du in Kommentaren verlangst. Der Autor bzw. die Autoren einer Funktion sind besser über das Versionskontrollsystem feststellbar, der Teststatus sollte durch Unit-Tests festgestellt werden. Und was soll das mit dem Sicherheitsstatus? Niemand wird eine Funktion schreiben, von der er weiß, dass sie das System zusammenkrachen lassen kann. Nein, Kommentare sollen bei Schnittstellen festhalten, wie sie zu benutzen sind und in Implementationsdetails erklären, was man sich bei der Umsetzung gedacht hat.
2.) Funktionen sollten kurz sein. Den Entwickler auf eine Zeilenanzahl festzunageln, ist indes unsinnig - die Zeilenanzahl ist eine schlechte Metrik für Codelänge, und es gibt Zusammenhänge, die sich nicht in 10 Zeilen erschlagen lassen. Wenn eine Fuktion länger als 20-30 Zeilen wird, ist das ein Warnsignal, aber auf Ausnahmen von der Regel muss man vorbereitet sein.
3.) Das soll ein Witz sein, oder?
4.) Rohe Zeiger sind zur Ressourcenverwaltung nicht geeignet, aber sie komplett zu verbieten, schießt weit über das Ziel hinaus - ganz besonders unter Performance-Gesichtspunkten. Soll man jedes Array in einen Vektor stopfen, bloß weil man sonst Zeiger in Standardalgorithmen stopfen müsste? Man kann C++ wie Java schreiben, aber wenn man das vorhat, hat man die falsche Sprache für das Projekt gewählt.
5.) Bei globalen Variablen stimme ich zu, aber alle Funktionen an Klassen zu ketten, scheint mir wenig sinnvoll. Der Satz über Java im vorherigen Punkt gilt auch hier - wofür gibt es denn Namensräume? Gegen globale Konstanten (echte Konstanten, zwecks Threadsicherheit) ist auch nichts zu sagen - es macht wenig Sinn, eine Lookup-Tabelle oder einen TST jedes mal neu aufzubauen, wenn man etwas nachschlagen will.
6.) Genau umgekehrt wird ein Schuh draus. Wie willst du denn Teile zu einer main zusammenfügen, die noch gar nicht existieren? Meiner Erfahrung nach ist es sinnvoller, von innen nach außen vorzugehen - wenn du eine Kalenderanwendung schreibst, fängst du mit der Datumsklasse an. Dann lässt sich bestehender Code auch besser (bzw. überhaupt) testen.
7.) Generell richtig, mindestens für öffentlich sichtbare Klassen. In Implementationsdetails tut's auch mal ein nacktes Struct.
8.) D'accord.
-
wxSkip schrieb:
2. Funktionen sollten maximal 5-10 Zeilen enthalten (es wird soviel gekapselt wie möglich)
Insbesondere beim numbercrunching kommt man da nicht weit. Da hast du dann permanent die Wahl zwischen: eine Funktion mit 15 Parametern oder dinge doppelt berechnen. Super. Und all das nur, weil eine Funktion nicht länger als 10 Zeilen sein darf.
Und selbs tin Anwendungslogik schätze ich, dass es den Code mit Hunderten von Bezeichnern zu müllen würde.
-
pumuckl schrieb:
- was muss die Klasse können?
- -> öffentliches Interface festlegen
- Unit-Tests schreiben und laufen lassen
- -> natürlich Linkerfehler
- implementieren
- Unit-Tests laufen lassen
- goto 6)

- Unit-Tests schreiben und laufen lassen -> Fail
- Minimale + falsche Implementierung schreiben
- Unit-Tests laufen lassen -> Fail, Test funktioniert
- Minimale + richtige Implementierung
- Unit-Tests laufen lassen -> Pass
- Refactoring
- Unit-Tests laufen lassen
- goto 6)

-
brotbernd schrieb:
pumuckl schrieb:
- implementieren
- Unit-Tests laufen lassen
- goto 6)

- Refactoring
- Unit-Tests laufen lassen
- goto 6)

Refactoring gehört bei mir zur Implementierung dazu. Ganz einfach deshalb, weil ich davon ausgehe, dass ein Implementierungsschritt mit einem genügend rafactored (?) Code beginnt und erst fertig ist, wenn nach dem neu implementierten Feature der Code wieder genügen refactored wurde.
Th69 schrieb:
oder laufen bei dir die Unit-Tests in einer Endlosschleife?

Natürlich nicht. Nur bei jedem noch so kleinen Build

-
OK, dann werd ich dazu wohl mal Stellung nehmen

Im Voraus: Ich habe wenig Erfahrung mit Richtlinien, bisher stelle ich die mir noch selbst und ich wende auch fast keine von denen an, die sind mir nur mal durch den Kopf gegangen, daher erhebe ich auch keinen Anspruch auf Vollständigkeit.
1. Klar, Funktionalität usw. gehört natürlich dazu. Ersteller über Verwaltungssystem, bei unzureichender Sicherheitsüberprüfung kann/sollte man stattdessen vermutlich eher ein TODO irgendwo festhalten. Für Getter und Setter ist das natürlich überzogen, höchstens für ein Doku-Tool.
2. Wie gesagt, ich habe es noch nie ausprobiert. Das sollte (wie goto usw. auch) kein hartes Limit sein, eher fragt man im Umkehrfall mal bei jemand anderem nach, ob es nicht noch besser geht. 30 Zeilen sind ja auch noch kurz. (je nach Aufgabe)
3. Hier stimme ich brotbernd zu. Das muss ja keine Umfrage hier im Forum sein, sondern entwicklungsintern oder erstmal nur einen Mitarbeiter.
4. @pumuckl: Wie gesagt, Ausnahme für Pointer-Implementierungen. Ansonsten gilt wie 2., dass das kein hartes Limit sein sollte. Wenn man ein 9*9-Sudoku-Array hat, muss man natürlich keinen std::array<9, std::array<9, uint8> > daraus machen. Allerdings sollte man sich dann auch im Klaren sein, dass man in einem rohen Array nicht so einfach Debug-Ausgaben realisieren kann. (im std::array auch schwer, aber wenn es dann an eigene Kleinklassen geht, wird das wesentlich einfacher)
5. Sorry, meinte natürlich freie Funktionen und nicht statische. (Hm... in Java heißt es ja static-Funktionen
) Was ich gegen freie Funktionen ohne namespace habe? Das weiß ich auch nicht so genau
. Ich fände es besser, sie zu gruppieren (sowohl in namespace als auch dann entsprechend in verschiedene Dateien) aber diese Regel finde ich nicht so wichtig. Globale Variablen sollte man sich wirklich genau überlegen und mindestens absprechen.
6. Das war wohl die gewagteste Aussage. Zur Teamarbeit: Wenn die Main-Funktion einmal implementiert ist und meinetwegen 5 Funktionen aufruft, kann man schon mal 5 Entwicklergruppen einteilen, die die Funktionen implementieren. Entsprechend könnte dann weiter aufgeteilt werden. Mit Tests usw. und ohne gute Planung geht das vermutlich nur sehr schwierig. Das Implementierungsverbot für nicht benutzte Funktionen/Klassen ist vermutlich zu hart, da gebe ich euch Recht.
7. + 8. Wie gesagt, Ausnahmen sind generell möglich, aber nicht unbedingt erwünscht.
-
wxSkip schrieb:
bei unzureichender Sicherheitsüberprüfung kann/sollte man stattdessen vermutlich eher ein TODO irgendwo festhalten.
Nein, man sollte es von vornherein richtig machen. Exceptionsicherheit wirkt sich auf alles aus, was du in einer Funktion machst. Wenn du sie nicht von Anfang an sicher schreibst, heißt dein "TODO", dass du sie nochmal zu 80% neu schreiben musst. Das ist dann wiederum eine so große Hürde, dass das Provisorium nie aufgelöst wird. Machs also gleich oder lass es ganz sein.
4. @pumuckl: Wie gesagt, Ausnahme für Pointer-Implementierungen. Ansonsten gilt wie 2., dass das kein hartes Limit sein sollte. Wenn man ein 9*9-Sudoku-Array hat, muss man natürlich keinen std::array<9, std::array<9, uint8> > daraus machen. Allerdings sollte man sich dann auch im Klaren sein, dass man in einem rohen Array nicht so einfach Debug-Ausgaben realisieren kann. (im std::array auch schwer, aber wenn es dann an eigene Kleinklassen geht, wird das wesentlich einfacher)
Wenn du an Klein- und Kleinstklassen wie einem Array schon debugausgaben anbringen willst, dann gute Nacht. Solche Klassen sind in größeren Projekten zu tausenden vertreten, und wenn da Debugausgaben kommen, heißt das Millionen von zeilen Debugmist, den keiner mehr lesen kann -> alles für die Katz. Mal abgesehen davon, dass ein "array.get() aufgerufen" nicht wirklich aussagekräftig sein kann.
-
pumuckl schrieb:
4. @pumuckl: Wie gesagt, Ausnahme für Pointer-Implementierungen. Ansonsten gilt wie 2., dass das kein hartes Limit sein sollte. Wenn man ein 9*9-Sudoku-Array hat, muss man natürlich keinen std::array<9, std::array<9, uint8> > daraus machen. Allerdings sollte man sich dann auch im Klaren sein, dass man in einem rohen Array nicht so einfach Debug-Ausgaben realisieren kann. (im std::array auch schwer, aber wenn es dann an eigene Kleinklassen geht, wird das wesentlich einfacher)
Wenn du an Klein- und Kleinstklassen wie einem Array schon debugausgaben anbringen willst, dann gute Nacht. Solche Klassen sind in größeren Projekten zu tausenden vertreten, und wenn da Debugausgaben kommen, heißt das Millionen von zeilen Debugmist, den keiner mehr lesen kann -> alles für die Katz. Mal abgesehen davon, dass ein "array.get() aufgerufen" nicht wirklich aussagekräftig sein kann.
Du könntest damit immerhin einen falschen Arrayzugriff feststellen/ausschließen, auch ohne Debugausgaben...