Hi,
nur mal vorab, meine Fragen haben nix mit Hausaufgaben oder sonst was,
was andere von mir verlangen zu tun. Just a few thoughts on behalf of
myself ;)
Also es geht los ..
Gibt es eine Empfehlung oder einen nennen wir es "Standard" an Umfang
von Codezeilen in einer Datei (einer source), die man als Programmierer
nicht überschreiten sollte? Ich meine jetzt vor allem bei
veröffentlichten Projekten, die ja meist einen gewissen Umfang
mitbringen, deren Quellcode jeder einsehen kann.
Ist das etwa auch noch von der Programmiersprache abhängig und falls ja,
warum? Also z.B. bei Python deutlich kritischer oder eher das Gegenteil?
Bei C vs. C++ identisch? Bei PASCAL (soll es ja auch noch geben ;))
vielleicht wegen der Geschwätzigkeit der Sprache sogar kritischer
ähnlich wie bei C#/.NET?
Ab welchem Umfang also leidet i.A. der Überblick von Quellcode-Dateien?
Spielen hier andere Faktoren noch eine Rolle, die man nicht außer Acht
lassen sollte. Falls ja, welche wären das?
Wie vermitteln Buchautoren in ihren Büchern für Programmieranfänger oder
Fortgeschrittene diese Thematik? Selten? Gar nicht? Oder doch sehr
eindringlich? Erfahrungen?
Wie also behält man am besten den Überblick vor lauter Codezeilen?
Debug schrieb:> Ab welchem Umfang also leidet i.A. der Überblick von Quellcode-Dateien?
Kommt schwerstens auf den Programmierer und die verwendete IDE an.
Moderne IDEs, die Dir die Struktur des Programms anzeigen können,
ermöglichen sinnvolles Arbeiten auch mit 10k+ Zeilen. Das Limit ist eher
der Programmierer selbst.
Debug schrieb:> Ab welchem Umfang also leidet i.A. der Überblick von Quellcode-Dateien?
Das kann man so nicht sagen. Das ist ganz individuell.
Bei manchem Code leidet die Übersicht schon bei 100 Zeilen und bei
manchem Code erst bei 10000 Zeilen.
Warum machst du dir überhaupt darüber gedanken?
Wenn du irgendwann denkt "Huch das wird aber unübersichtlich", dann
verschiebst du einfach Teile in andere Dateien und gut.
Ich meine mich zu erinnern, dass ich mal las, dass Codeteile nur eine
Aufgabe erfüllen sollten. Das Programm sollte demnach so aufgeteilt
werden, dass eine Funktion eine Aufgabe hat. So lässt sich der Code wohl
recht sinnvoll strukturieren.
Das einzige gute Buch, dass ich kenne, das ueber das Thema physikalische
Codestruktur handelt, ist John Lakos Large scale c++ Design. Immer noch
empfehlenswert, eine neue Auflage ist angekuendigt.
Die Empfehlung da ist: pro datei eine Klasse.
Unabhaengig von dem Buch gibt es auch ein paar schoene Vortraege auf
youtube.
Teile und herrsche:
In C kannst Du Quelltext für ein Projekt aufteilen in
Libraries (alles was unabhängig von der konkreten Applikation, z.b. HAL,
Grafikpakete, stdlib...)
C-files
Funktionen.
Wenn alles unabhängige in libraries versteckt ist, wie viele Codezeilen
bleiben dann? 1000? Dann z.b. 10 Files je 10 Funktionen je 10 Zeilen,
wie im Lehrbuch. (Pareto-verteilr, s.u.)
1Mio? Dann eher 100 Files je 100 Funktionen je 100 Zeilen.
Das ganze dann (wie alles natürliche) etwa pareto verteilt, also z.b.
20% der Files enthalten 80% des Codes. Darum gibt's auch Funktionen mit
mehreren 10.000-zeilen und vielleicht ein File mit 100.000 Zeilen. Du
wirst erkennen, welche Funktionen klein bleiben sollten (z.b. abs) und
welche wiederkehrende Muster enthalten, die aufzubrechen mehr schadet
als nutzt.
Das es für riesige Codeprojekte weitere Mechanismen gibt, den Code zu
reduzieren, (Datenkonfiguration, metasprachen, automatische Kopien, ...)
ändert nichts am Verhalten, wenn am Ende 1 Mio Zeilen notwendig sind.
Wenn das Projekt hingegen aus 10 unabhangigen Teilprojekten je 100.000
Zeilen besteht, dann sollten die auch getrennt bleiben.
Dumdi D. schrieb:> Die Empfehlung da ist: pro datei eine Klasse.
Ganz genau! Das ist eine äußerst sinnvolle Daumenregel.
In meinem Code gibt es eine Klasse deren Implementierung schon über 5000
Zeilen hat. Natürlich bin ich bestrebt, Dateien deutliche kleiner als
das zu halten, aber in diesem Fall macht es einfach Sinn. Und die IDE
hilft, sich zurechtzufinden.
Früher hätte ich so eine Datei aufgeteilt, und ich habe das bei einer
ähnlich umfangreichen Klasse auch gemacht. Es hat sich nicht bewährt:
Man sucht die Funktionen immer in der falschen Datei.
tictactoe schrieb:> Ganz genau! Das ist eine äußerst sinnvolle Daumenregel.
Ich habe das immer für eine Selbstverständlichkeit gehalten. Inclusive
eines Namensschemas für Klasse, Dateiname von cpp und Header. Auf die
Idee, eine Klasse in verschiedene Dateien zu zerpflücken, spätestens
seit brief, wäre ich nicht gekommen.
Das Problem sehe ich quasi eine Ebene weiter: Mache ich mehrere Klassen,
wenn eine zu groß wird. Also nicht, weil zuviele verschiedene Dinge
vermischt werden (dann natürlich trennen), sondern weil eine Menge
gleichartiger Dinge sich anhäufen. Dann nein. Wenn ich (dummes Beispiel)
eine Jahres-Klasse habe, die für jedes Datum (366-Tage) eine eigene
Methode von 100-200 Zeilen hat (warum auch immer), dann zerreiße ich das
nicht in die Klassen Frühjahr, Sommer, Herbst und Winter, um irgendeine
Metrik einzuhalten.
Debug schrieb:> Gibt es eine Empfehlung oder einen nennen wir es "Standard" an Umfang> von Codezeilen in einer Datei (einer source), die man als Programmierer> nicht überschreiten sollte
Nein.
Solche Empfehlungen wären frei von jeder fachlichen Begründung.
Ein ordentliches C-Programm mit 1 Mio Zeilen in Form einer Datei kann
angemessen sein, dazu muss es gar keine statische Definition einer 1 Mio
Zeilen Tabelle oder autogenerierter Parser einer EBNF Syntax sein,
sondern könnte ein echtes Programm sein.
Vorteile wären: Kaum Overhead (nicht dutzend Datein in die wieder und
wieder stdio.h eingebunden werden muss), keine eigene h Datei, man weiss
spontan was zum Projekt gehört, build sogar ohne make einfach, schnelles
Auffinden z.B. von Definitions und Aufrufstellen durch
Editor-Suchfunktion.
Nachteile: Man merkt schnell, welche Programierwerkzeuge nichts taugen
weil sie mit der Dateigrösse nicht klar komnen, also nicht überallhin
portabel. Nachteil auch dass nicht mehrere Leute an getrennten Bereichen
des Programms arbeiten können weil Modularisierung fehlt.
Bei vielen C++ Programmen nervt das Gegenteil: Ein unsäglicher Verhau
von winzigsten Dateien mit nur 1 Methodendefinition, möglichst noch mit
langen Dateinamen (vorne alle gleich beginnend und sich erst
unterscheidend wo die Anzeige schon abgeschnitten wird) über dutzende
Unterverzeichnisse verteilt, dazu kreuz und quer verwoben über
header-Files in denen letztlich der ganze ausführbare code steht,
möglichst als template.
Irgendwo dazwischen wäte eine sinnvolle Aufzeilung, vor allem dann
sinnvoll wenn es für den Schnitt einen fachlichen Grund gibt, z.B. eine
Klasse pro Datei, ein herkopiertes Modul, aber sicher nicht: "Weil 100
Zeilen erreicht wurden, hat mal jemand so empfohlen".
Achim S. schrieb:> dann zerreiße ich das nicht [...] um irgendeine Metrik einzuhalten.
Zur Ergänzung: Das gegenüber Tüv, Kunden oder Zertifizierern zu
vertreten, ist meist einfacher als gegenüber den Experten der eigenen
Abteilung, die trotz großer Erfahrung mangels Verständnis von wichtigen
Projekten weggehalten werden und stattdessen Code-Guidelines vom
Allerfeinsten in Stein meißeln, teilweise mit Autorität nach Dilberts
Prinzip.
Achim S. schrieb:> einfacher als gegenüber den Experten der eigenen Abteilung, die trotz> großer Erfahrung mangels Verständnis von wichtigen Projekten weggehalten> werden u
Kannst Du das erklaeren? Fuer mich hoert es sich doch richtig an falls
Leute ohne Verstaeendnis (auch wenn sie ansonsten Experten sind) von
wichtigen Projekten ferngehalten werden. Oder war 'Experte' ironisch
gemeint?
Als ich mich früher mit der IBM ICLUI beschäfftigt hatte gab es die
Empfehlung nicht mehr als 20 Zeilen in eine Methode zu packen. Ist schon
lange her, eine Quelle kann ich da nicht nennen.
Lesenswert zu diesem Thema ist das Buch "Clean Code" von Robert C.
Martin.
Wie mans macht, ist es falsch...
In einer Bibliothek haben wir z. B. sehr große Quellcode-Dateien mit
5000 bis 10000 Zeilen (historisch gewachsene Gott-Klassen, die bisher
nicht bereinigt wurden). Da findet man nichts wieder, weil man ständig
rumscrollen muss.
In einer anderen Bibliothek ist keine Datei länger als ca. 500 Zeilen,
eher weniger. Da findet man auch nichts, da alles über viele Dateien
verstreut ist.
Dumdi D. schrieb:> Oder war 'Experte' ironisch gemeint?
Ironisch wäre das falsche Wort. Ja, es sind Experten, mit langer
Erfahrung, mit erfolgreichen Projekten, mit großen Meriten, mit großer
Reputation, ... und doch ist der Quelltext ein graus, ist die
Entwicklung zäh, die Fehlersuche sisyphoesk, ... auch weil sie noch
Anfängerregeln nachhängen, wie "sprechende Sätze als Namen",
"einstellige McCabe", "Funktionen zerfransen auf Bildschirmgröße".
Und schaut man auf "Erfolge und Meriten", so wurde allein vom Aufwand
auf die Komplexität der Arbeit rückgeschlossen. Und "solide Arbeit,
keine Beschwerden, keine Nacharbeit" bedeutet meist: Der Kunde hat nach
3 nervigen Iterationen die Schnauze voll und hofft nicht mehr auf
Verbesserung.
Nur brauchbare Software wird gebraucht. Und je schneller sie verbessert
wird, umso mehr Schwächen/Wünsche/Reklamationen werden gemeldet.
Ich denke man sollte nicht so heran gehen, und hauptsächlich die
Dateigrösse im Focus haben. Wenn man ein Programm logisch gut
wiederverwendbar strukturiert, dürften sich in den meisten Fallen auch
vernüftige Funktions-/Methoden- und Dateigrössen ergeben.
Die Faustregel eine Klasse pro Datei finde ich nicht verkehrt. Kann man
auch in C anwenden. Ok, Vererbung, Templates z.B. gibt es in C nicht,
aber dennoch kann man grundlegend objektorientiert auch in C
programmieren.
Dann hatte ich mal irgendwo die Empfehlung gelesen, das eine
Funktion/Methode ca. eine Bildschirmseite gross sein sollte. Die
Empfehlung hinkt natürlich etwas, bei den ganzen unterschiedlichen
Bildschirmgrössen, aber es ist richtig für übersichtliche Funktionen zu
sorgen, deren Arbeitsweise schnell erfassbar ist.
Barex schrieb:> Dann hatte ich mal irgendwo die Empfehlung gelesen, das eine> Funktion/Methode ca. eine Bildschirmseite gross sein sollte.
Die Regel ist ausschließlich für Anfänger und Anfänger-Aufgaben
sinnvoll, damit sie nicht versuchen, verschiedene Aufgaben in einer
Gott-Funktion zu erschlagen.
für (bezahlte) SW-Entwickler mag die Intention dahinter sinnvoll sein,
Funktionen möglichst kurz zu halten und für größeres datengetriebene
Strukturen zu verwenden. Als Regel aber ist es völlig sinnfrei!
Eine Funktion wie abs ist sinnvoll. Damit ist eine Zeile Funktions-Code
die untere Grenze.
Andere, die z.B. in Abhängigkeit eines Kommandos oder Telegrammtyps
leicht unterschiedliche Dinge tun, können problemlos 100 cases a 10
Zeilen enthalten (wenn datengetriebene Strategien nicht möglich sind).
Zerreißen in Cmd_1() ... Cmd_100(), oder gar in Cmd_0x(), Cmd_1x(),
Cmd_2x(), ist dann richtig übel. Bei Anfänger-Aufgaben mit 3-5
Subfunktionen, ist es völlig egal, ob ich eine große oder 6 kleine
Funktionen habe.
Nun ja, ich hätte dazuschreiben sollen, dass man das nicht als enge
Regel sondern als Richtlinie sehen sollte.
Für übersichtliche Funktionen zu sorgen, halte ich nicht nur für
Anfänger für sinnvoll. Ich möchte jedenfalls keine Funktion warten
müssen, die aus mehreren 100 Zeilen Code besteht.
Untruthful Slime Ball schrieb:> Lesenswert zu diesem Thema ist das Buch "Clean Code" von Robert C.> Martin.>> Wie mans macht, ist es falsch...>> In einer Bibliothek haben wir z. B. sehr große Quellcode-Dateien mit> 5000 bis 10000 Zeilen (historisch gewachsene Gott-Klassen, die bisher> nicht bereinigt wurden). Da findet man nichts wieder, weil man ständig> rumscrollen muss.>> In einer anderen Bibliothek ist keine Datei länger als ca. 500 Zeilen,> eher weniger. Da findet man auch nichts, da alles über viele Dateien> verstreut ist.
Das scheint das Grundproblem zu sein. Wobei die endlose Scrollerei doch
schon weit früher anfängt. Ausgangspunkt meiner Anfrage war der Blick in
Sourcen, wie z.B. diese hier
https://github.com/FreeCAD/FreeCAD/blob/master/src/App/Application.cpphttps://github.com/FreeCAD/FreeCAD/blob/master/src/Gui/CommandView.cpp
Ich hab gar nicht gezählt, wie viele solcher Codezeilenmonster noch in
den Verzeichnisbäumen herumlungern. Und da soll wie es immer so schön
heißt jemand "mal eben" als Außenstehender noch was "verbessern" können?
Das kann mir keiner erzählen.
Johannes S. schrieb:> Als ich mich früher mit der IBM ICLUI beschäfftigt hatte gab es die> Empfehlung nicht mehr als 20 Zeilen in eine Methode zu packen. Ist schon> lange her, eine Quelle kann ich da nicht nennen.
Wobei das Problem der Codeübersicht ja nicht nur bei Methoden (C++) oder
Funktionen (C) besteht, sondern vor allem in der schieren Masse an
Methoden, die da in so einer Application.cpp herumlungern. Mitten drin
bei Zeile 2410 kommen dann plötzlich wieder neue includes. Da wird doch
der Hund in der Pfanne verrückt. Muss sowas hier wirklich sein?
Die ganze Diskussion läuft doch ein wenig auf folgendes Problem hinaus:
Es soll ein ein großes Programm entwickelt werden, das voraussichtlich
etwa 1 Million Codezeilen umfassen wird. Wie sollen diese ganz grob auf
einzelne Dateien verteilt werden?
100 Dateien à 10000 Zeilen?
1000 Dateien à 1000 Zeilen?
10000 Dateien à 100 Zeilen?
Ohne geeignete Navigationstools ist wohl keine der drei Alternativen zu
beherrschen. Entweder sucht man ewig nach einer Datei oder ewig nach
einem Codeabschnitt innerhalb einer Datei oder beides.
Verfügt man hingegen über die richtigen Tools, sind weder Anzahl noch
Größe der Dateien von großer Bedeutung. Man macht die Aufteilung dann
ausschließlich nach logischen Gesichtpunkten. Je nach Kontext ist eine
logische Einheit meist eine Klasse oder auch eine Gruppe von kleineren
Klassen, die in einem engen Zusammenhang zueinander stehen.
Unabhängig davon sollte man auch versuchen, den Code möglichst prägnant
zu formulieren, also bspw. die Duplizierung größerer Codeabschnitte und
unnötige Fallunterscheidungen zu vermeiden. Das reduziert nicht nur die
Anzahl der Codezeilen, sondern auch die Komplexität der einzelnen
Funktionen, so dass die Verständlichkeit sogar in doppeltem Maße steigt.
Auch die Wahl der Programmiersprache hat einen nicht unerheblichen
Einfluss auf die Größe und Verständlichkeit des Codes.
Yalu X. schrieb:> Verfügt man hingegen über die richtigen Tools, sind weder Anzahl noch> Größe der Dateien von großer Bedeutung. Man macht die Aufteilung dann> ausschließlich nach logischen Gesichtpunkten. Je nach Kontext ist eine> logische Einheit meist eine Klasse oder auch eine Gruppe von kleineren> Klassen, die in einem engen Zusammenhang zueinander stehen.
Sehe ich genauso: früher - als solche Regeln entstanden - hatte man
keine Tools, um das gescheit zu beherrschen. Heute hat man die Tools,
deswegen sind solche Regeln egal.
Aber: trotzdem gelten die Prinzipien wie etwas SRP, möglichst lose
Koppelung und hohe Kohäsion.
Wilhelm M. schrieb:> Sehe ich genauso: früher - als solche Regeln entstanden - hatte man> keine Tools, um das gescheit zu beherrschen.
emacs und vi gabs schon immer.
Oliver
Oliver S. schrieb:> Wilhelm M. schrieb:>> Sehe ich genauso: früher - als solche Regeln entstanden - hatte man>> keine Tools, um das gescheit zu beherrschen.>> emacs und vi gabs schon immer.
Immer nicht: vor dem emacs und dem vi gab (und gibt) es den ed ...
Auch wenn emacs und vi(m) heute super Werkzeuge sind, die anderen IDEs
kaum nachstehen (ausser vielleicht in der Optik ;-)), so war das aber
nicht immer so. Kann mich noch gut an die Zeiten von emacs/ctags
erinnern ... aber sourcetrail, etc. oder auch die language-engines
einiger IDEs inkl. der UI-Komponenten sind da schon eine andere Liga.
Und weil durch solche Tools eben die Abbildung der 100KLoC auf N Dateien
egal ist, sind solche Regeln heute kein geeignetes Maß für Qualität
mehr. Was viel wichtiger ist, sind die von mir o.g. Punkte wie SRP, lose
Koppelung und starke Kohäsion oder auch solche salopp formulierten
Regeln wie etwa "Eine Schnittstelle sollte leicht richtig und nur schwer
falsch zu benutzen sein." oder auch "Vollkommenheit ist nicht dann
erreicht, wenn man nichts mehr hinzufügen kann, sondern wenn man nichts
mehr entfernen kann."
Linker können meist nur ganzen Files einbinden
ist ein Element des Files referenziert wird alles eingebunden
auch die nicht referenzierten.
Weshalb man bei Bibliotheken generelle möglichst je Funktion ein File
macht.
C Kennt ja bei den Sichtbarkeiten nur die Einteilung:
Funktionslokal
und File weit
Über Include-Regeln kann man die Sichtbarkeit zwischen den Files regeln
dadurch ergibt sich schon ein Minimum an Aufteilung
Volle2 schrieb:> meist
Meistens sind Pauschalaussagen zwar irgendwie zutreffend, im
Zweifelsfall aber falsch. Seit einigen Generationen geht da schon etwas
mehr zwischen Compiler und linker.
Beim gcc z.B per -ffunction-sections -fdata-sections oder gleich -flto.
Oliver