Das ist doch eigentlich nicht schwer...DeathAndPain hat geschrieben: ↑16.01.2020 15:49Ich habe schon eine Menge Inline-Deklarationen gesehen, die die Verständlichkeit des Codes deutlich erschwert haben, weil man erst mal ins Grübeln gekommen ist, welcher Datentyp da jetzt eigentlich bei rauskommt.
Code: Alles auswählen.
DATA(excluded_functions) = VALUE ui_functions(
( 'CHANGE' )
( 'SAVE' )
( 'SOMETHING' ) ).
Dem muss ich mal widersprechen. Wenn man OO einsetzt, wie es wirklich mal gedacht war von Kay, delegiert man nur Messages. Ein Aufrufer bearbeitet selbst nichts. Das führt automatisch zu kürzeren und atomisierten Einheiten.DeathAndPain hat geschrieben: ↑16.01.2020 10:17
Und bitte jetzt nicht das Argument, dass alle Methoden nur 10 Zeilen lang sein sollten, so dass alles als lokal einzustufen ist. Dieses Paradigma halte ich für ebenso gutaussehend wie praxisuntauglich, denn es führt dazu, dass Fremdcode kaum mehr nachvollziehbar ist - obwohl es paradoxerweise genau das Gegenteil erreichen soll! Hintergrund ist, dass eine Unterroutine eine ähnliche Bedeutung hat wie ein selbst definierter Befehl.
Aber normale Felddeklarationen am Anfang der Routine durch Inlines ersetzen? Ne.
Auch hier ein Veto noch :DeathAndPain hat geschrieben: ↑16.01.2020 10:17
Deswegen bin ich der Meinung, dass es sinnvoll ist, seinen Code in sinnvolle Verarbeitungsblöcke zu gliedern, diese als Unterroutinen auszuführen - und jede einzelne davon mit einem ausführlichen Textkommentar am Kopf der Routine zu versehen, der erläutert, was diese Routine genau macht und was rein- und rausgeht.
Wenn man aber jeden kleinen 5-Zeiler dieserart kapselt, ist das nicht mehr praktikabel, und man erreicht das Gegenteil von dem, was man erreichen möchte. Davon abgesehen hat Otto Normalprogrammierer ja noch nicht einmal die Disziplin, seine normalen Routinen mit einem anständigen Kopfkommentar zu versehen (selbst Klassen, öffentlichen Methoden und Funktionsbausteine sind ja erschreckend oft ohne jede Online-Doku). Da ist es realitätsfernes Wunschdenken anzunehmen, dass die Leute jedem mutwillig untergekapselten Fünfzeiler eine eigene Doku zukommen lassen, damit man beim Sichten der Aufrufstelle versteht, was dieser Aufruf genau tut. Aber selbst, wenn sie es täten, ist bei einer derart feinen Granulierung das Programm unübersichtlich, wenn man nur Aufrufstellen und zugehörigen Kommentartext (der hoffentlich stimmt und ausführlich genug ist) liest anstelle von echtem Code, bei dem man "sieht", was er macht.
Dein Gedanke ist grundsätzlich erstmal nachvollziehbar. Leider ist es trotzdem der falsche Ansatz nach meiner Meinung. Erweiterbarkeit, Wiederverwendung und Austauschbarkeit sind hier nicht gewährleistet. Geschweige denn, das Unit-Tests möglich sind.DeathAndPain hat geschrieben: ↑16.01.2020 12:09
Wenn eine Prozedur aus mehreren, linear aufeinanderfolgenden und absehbar nirgendwo anders benötigten Schritten besteht, dann entschließe ich mich auch gerne mal dazu, diese Schritte nicht zu kapseln, sondern mit deutlich sichtbaren Kommentartrennlinien voneinander zu trennen. Dann sieht man als Leser, wie ein Schritt auf den anderen folgt und das Ganze dennoch eine Einheit bildet. Natürlich darf die Gesamtlänge dabei nicht völlig aus dem Ruder laufen.
Und danach dann die Implementierungen von PASS1 und PASS2. Das finde ich aber affig; mit dieser Kapselung gewinnt man nicht wirklich viel. Da mache ich lieber:Code: Alles auswählen.
FORM WRITE_DATA. PERFORM PASS1. PERFORM PASS2. ENDFORM.
Auch wenn jeder der beiden Pässe 50 Zeilen Code lang ist.Code: Alles auswählen.
FORM WRITE_DATA. ********** PASS 1 ********** " Actual code of pass 1 ********** PASS 2 ********** " Actual code of pass 2 ENDFORM.
😇 Für deine Ausführungen gibt es eine schöne Beschreibung in einem recht sehenswerten englischen Youtube Video über Anti-Patterns ( Anti-Entwurfsmuster ).DeathAndPain hat geschrieben: ↑16.01.2020 10:17[...]Und bitte jetzt nicht das Argument, dass alle Methoden nur 10 Zeilen lang sein sollten, so dass alles als lokal einzustufen ist. Dieses Paradigma halte ich für ebenso gutaussehend wie praxisuntauglich, denn es führt dazu, dass Fremdcode kaum mehr nachvollziehbar ist - obwohl es paradoxerweise genau das Gegenteil erreichen soll! Hintergrund ist, dass eine Unterroutine eine ähnliche Bedeutung hat wie ein selbst definierter Befehl. Die normalen ABAP-Befehle kennt jeder. Wenn da also 10 Zeilen normalen Codes stehen, kannst Du den normalerweise lesen und interpretieren. Wenn der Code aber nur aus Aufrufen irgendwelcher Unterroutinen besteht, die selber wieder aus Aufrufen bestehen, und das Ganze so weit runter, bis Du auf der Ebene, wo echter Code steht, keinen Kontext mehr siehst, dann ist das im Prinzip, also würdest Du eine Programmiersprache lesen, die Du nicht kennst (und die möglicherweise sogar Bugs enthält (jenseits von dem, was produktiv genutzte Programmiersprachen an Rest-Bugs zu haben pflegen)). [...]
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag (Insgesamt 3):
gtoXX • DeathAndPain • Legxis
Das meinte ich, als ich sagte: "Die einzige theoretisch mögliche Wechselwirkung wäre ein vergessener CLEAR zwischen den Pässen. Aber der ist für mich kein ausreichendes Entscheidungskriterium."ewx hat geschrieben:Du verwendest aber evtl. gleiche Variablen in Block A und B. Du kannst also mit einer Änderung in Block A das Verhalten von Block B verändern, ohne dass es dir bewusst ist!
Das ist ein Problem, das bei mir gleichfalls nicht auftritt. Wenn ich in Block 2 etwas debuggen will, dann setze ich den Breakpoint dort. Block 1 mag davor stehen, aber da kein Breakpoint drin ist, läuft das System da einfach durch und bleibt trotzdem nur in Block 2 stehen. Das Systemverhalten ist also nicht anders, als wenn Block 1 vorher als getrennte Unterroutine abgehandelt werden würde.ewx hat geschrieben:Ein weiterer wichtiger Punkt, den ich bei kleinen Einheiten zu schätzen gelernt habe: Wenn du debuggen musst, dann musst du bei großen Code-Blöcken durch die meisten Anweisungen im Einzelschritt durch. Selbst wenn es nur drei Mal "F5" für drei Anweisungen ist anstatt ein Mal "F6" für die Funktion, die das gleiche tut, ist das im Stress und unter Zeitdruck eine enorme Hilfe. Ich finde es sehr hilfreich, wenn ich schnell über eine Funktion "hinweg-debuggen" kann, weil ich weiß, dass hier nichts Fehlerrelevantes passiert.
Glaubst Du das? Es geht z.B. genauso die Zuweisung einer funktionalen Methode mit einem riesigen Rattenschwanz an Parametern. Dann musste erst mal schauen, wie die parametrisiert ist, bevor Du den Datentyp Deiner neuen Variable kennst. Zudem ist die "Zuweisungszeile" dann soundsoviel Bildschirmzeilen lang. Das ist um Größenordnungen unübersichtlicher als ein klares DATA xyz TYPE abc. Du bist gerade in einem völlig anderen Codeabschnitt und denkst über was völlig anderes nach, was mit dieser funktionalen Methode gar nichts zu tun hat, wirst aber aus diesen Gedankengängen herausgerissen, weil Du den Typ des Feldes klären möchtest.ewx hat geschrieben:Die Inline-Deklaration ohne genaue Typ-Definition ist sehr eingeschränkt:
- Integer (Zahl)
- String (Literal in Backticks `)
- Character (Literal in Hochkomma ' )
Das ist Theorycrafting, wie auch Kay ein Akademiker war. In der Praxis hast Du zwar bedingt recht, aber Code, den man gelesen oder ausreichend überflogen hat, so dass einem weitestgehend klar ist, was da passiert, kann man gedanklich tatsächlich abhaken. Bei dem anderen stellt man sich immer wieder die Frage: "Habe ich jetzt richtig interpretiert, was SCHREIBE_MATERIALDATEN sicherlich machen wird? Oder muss ich da doch noch mal einsteigen und noch tiefer in die Verästelungen runter, um zu analysieren, was die Befehle, äh, Unterprogramme, die da definiert werden, eigentlich tun?"ewx hat geschrieben:Dem muss ich mal widersprechen. Wenn man OO einsetzt, wie es wirklich mal gedacht war von Kay, delegiert man nur Messages. Ein Aufrufer bearbeitet selbst nichts. Das führt automatisch zu kürzeren und atomisierten Einheiten.
Die Methoden können sinnvolle Namen bekommen, was wiederum die Lesbarkeit von Fremdcode deutlich erhöht, statt durch 100 Zeilen code zu gehen und jede kleine Annweisung im Detail zu lesen, interpretieren und gedanklich abhaken, was man bewußt oder unbewusst macht.
So einen Unfug habe ich schon lange nicht mehr gehört. Gute Ausrede für Entwickler, die zu faul zum Dokumentieren sind. Aber fremder Code, der ja immer auch mit fremden Philosophien geschrieben worden ist, ist ohne Kommentar unverständlich. Zumal nicht nur die grobe Aufgabe der Routine, wie ich sie im (maximal dreißigzeichigen) Namen skizzieren kann, von Bedeutung ist, sondern auch der genaue Inhalt ihrer Parameter, also wann welcher Parameter welchen Wert haben sollte bzw. im Fall von Rückgabeparametern welchen Wert haben wird.gtoXX hat geschrieben:Ausführliche Textkommentare bedeuten schlechten Code um es mal provokant zu sagen. Wenn ich Parameter beschreiben muss verschwende ich Zeit und Geld.
Wenn eine Routine sinnvoll benannt ist, benötige ich keinerlei Kommentare.
Du hast es nicht verstanden, und zwei andere, die sich bei Dir dafür bedankt haben, demnach auch nicht.gtoXX hat geschrieben:Dein Gedanke ist grundsätzlich erstmal nachvollziehbar. Leider ist es trotzdem der falsche Ansatz nach meiner Meinung. Erweiterbarkeit, Wiederverwendung und Austauschbarkeit sind hier nicht gewährleistet. Geschweige denn, das Unit-Tests möglich sind.
Denn bei einer Änderung an der Form WRITE_DATA sollten Pass1 und Pass2 noch immer das Gleiche tun wie vorher.
Herrlich beschrieben! 😎black_adept hat geschrieben:😇 Für deine Ausführungen gibt es eine schöne Beschreibung in einem recht sehenswerten englischen Youtube Video über Anti-Patterns ( Anti-Entwurfsmuster ).
Wer direkt einsteigen will: Bei Minute 1:57 ( direkt nach den Vorbemerkungen zu Anti-Patterns ) wird mit dem Antipattern 1 genau das beschrieben inklusive der Konsequenzen.
Ich glaube, Du hast noch nicht einmal verstanden, dass die von black_adept verlinkte Videostelle ab Minute 1:57 mir genau das Wort redet. Und das, obwohl er das explizit noch dazugesagt hat...gtoXX hat geschrieben:Und einfach weil es so schön Lesenswert ist :
https://de.wikipedia.org/wiki/Anti-Pattern
Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag (Insgesamt 2):
Ichse2 • Legxis
Das stimmt. Das hatte ich vergessen.DeathAndPain hat geschrieben: ↑17.01.2020 13:38Es geht z.B. genauso die Zuweisung einer funktionalen Methode mit einem riesigen Rattenschwanz an Parametern.
Ein klassisches Beispiel für Codebloating. Unnütze Codezeilen wie Clearings, die bei klarer Trennung nicht notwendig sind.DeathAndPain hat geschrieben: ↑17.01.2020 13:38Das meinte ich, als ich sagte: "Die einzige theoretisch mögliche Wechselwirkung wäre ein vergessener CLEAR zwischen den Pässen. Aber der ist für mich kein ausreichendes Entscheidungskriterium."Black_adept hat geschrieben:Du verwendest aber evtl. gleiche Variablen in Block A und B. Du kannst also mit einer Änderung in Block A das Verhalten von Block B verändern, ohne dass es dir bewusst ist!
Natürlich sollte Block B nicht mit den Werten aus Block A weiterarbeiten, sondern eigenständig sein.
Also das ist eine sehr unüberlegtes Argument. Methoden zu verwenden, über deren Ergebnis sich man vorher nicht informiert hat, wäre absolut unüberlegt. Natürlich weisst Du welchen Typ dein Ergebnis hat.DeathAndPain hat geschrieben: ↑17.01.2020 13:38
Glaubst Du das? Es geht z.B. genauso die Zuweisung einer funktionalen Methode mit einem riesigen Rattenschwanz an Parametern. Dann musste erst mal schauen, wie die parametrisiert ist, bevor Du den Datentyp Deiner neuen Variable kennst. Zudem ist die "Zuweisungszeile" dann soundsoviel Bildschirmzeilen lang. Das ist um Größenordnungen unübersichtlicher ...
Wenn Du es mal eingesetzt hättest, würdest Du sehen, das es von Theocrafting weit entfernt ist. Klar, wenn ich nur write-and-forget-code schreibe mag es übertrieben erscheinen.DeathAndPain hat geschrieben: ↑17.01.2020 13:38Das ist Theorycrafting, wie auch Kay ein Akademiker war. In der Praxis hast Du zwar bedingt recht, aber Code, den man gelesen oder ausreichend überflogen hat, so dass einem weitestgehend klar ist, was da passiert, kann man gedanklich tatsächlich abhaken. Bei dem anderen stellt man sich immer wieder die Frage: "Habe ich jetzt richtig interpretiert, was SCHREIBE_MATERIALDATEN sicherlich machen wird? Oder muss ich da doch noch mal einsteigen und noch tiefer in die Verästelungen runter, um zu analysieren, was die Befehle, äh, Unterprogramme, die da definiert werden, eigentlich tun?"
Das ist die Realität, so wie ich sie erlebe. Die Tatsache, dass Routinennamen in ABAP auf gerade mal 30 Zeichen beschränkt sind, lässt die Annahme, man könne damit ausdrücken, was sie genau bewerkstelligen, als geradezu zynisch erscheinen. Aber selbst, wenn es 80 Zeichen wären, würde das nicht ausreichen, um demjenigen, der das Programm nicht kennt, ausreichend genau zu vermitteln, was die Routine tut und was die Bedeutung der Felder ist, die rein- und rausgehen. Den tatsächlichen Code zu begutachten mag aufwendig sein, aber wenn ich den untersucht habe, dann weiß ich, was da passiert.
Vor allem, weil man fremden Code meist dann begutachten muss, wenn er nicht das tut, was er soll, wenn also irgendwo ein Bug drin ist. Da ist es dann blöd, wenn ich mich auf mein Verständnis des Namens einer Unterroutine verlasse, damit sogar recht habe, aber diese aufgrund eines Bugs eben doch nicht genau das macht, was ich erwarte und ich rätselrate, ob das Programmverhalten an der Stelle, die ich gerade untersuche, falsch ist oder nur meine Einschätzung des Sollverhaltens der Routine nicht stimmt. Bei ABAP-Befehlen weiß ich genau, was sie tun, und Bugs auf dieser Ebene sind auch nahezu auszuschließen. Da steht man auf viel sichererem Boden.
Wohlgemerkt: ich rede keinem unmodularisierten Spaghetticode das Wort. Aber die Aufteilung in Unterprogramme sollte so gewählt sein, das jedes eine klar definierte Aufgabe umfasst, die dann auch sorgsam zu dokumentieren sich lohnt. Dann kann der Leser sich diese Doku (da können 3 Sätze reichen) durchlesen, hat eine Vorstellung davon, was diese Routine macht, weiß, dass diese Vorstellung richtig ist, da sie nicht nur auf seiner Interpretation von Prozedurnamen beruht, und kann abhängig von seiner Problemstellung beurteilen, ob er in diese Routine tiefer einsteigen muss oder sein Problem höchstwahrscheinlich an anderer Stelle zu finden sein wird.
1. Für mich sind zuviele Kommentare ein Zeichen für Faulheit zum sinnvollen ProgrammdesignDeathAndPain hat geschrieben: ↑17.01.2020 13:38So einen Unfug habe ich schon lange nicht mehr gehört. Gute Ausrede für Entwickler, die zu faul zum Dokumentieren sind. Aber fremder Code, der ja immer auch mit fremden Philosophien geschrieben worden ist, ist ohne Kommentar unverständlich. Zumal nicht nur die grobe Aufgabe der Routine, wie ich sie im (maximal dreißigzeichigen) Namen skizzieren kann, von Bedeutung ist, sondern auch der genaue Inhalt ihrer Parameter, also wann welcher Parameter welchen Wert haben sollte bzw. im Fall von Rückgabeparametern welchen Wert haben wird.
Ich habe es sehr wohl verstanden. Du hast 2 funktionale Einheiten, die in einem Programmteil benutzt werden. Was Du nicht verstanden hast ist : Wie sie das tun was Du willst, kann deiner verwendenden Routine egal sein. Und wenn sie angepasst werden müssen um noch zu funktionieren, funktioniert das in deinem Coding nicht, weil sie nicht autark sind. Ich muss dazu noch verstehen, was dein Umfeld-Coding macht um nicht aus Versehen etwas kaputt zu machen. Bei einer vernünftigen Trennung ist das nicht nötig.DeathAndPain hat geschrieben: ↑17.01.2020 13:38Du hast es nicht verstanden, und zwei andere, die sich bei Dir dafür bedankt haben, demnach auch nicht.
Ad 1.) ZU viel von jedem ist schlecht. Sogar Sauerstoff . Aber ausführliche Kommentare sind kein Zeichen von Faulheit sondern einfach nett gegenüber denjenigen die ein Programm warten sollen. Ich freue mich immer wieder über Codestrecken wo ein "Siehe Ticket .... Folgendes Problem war damals aufgetaucht.... Anforderer war... , Folgender Lösungsansatz wurde gewählt weil... " oder auch nur Teile davon stehen. Wenn nicht hätte ich desweilen den einen oder anderen Code entsorgt weil er sinnlos aussah aber der Kommentar hat mir verraten dass dem in diesem speziellen Fall nicht so war. Oder an wen ich mich wenden konnte wenn wir weiter an der Ecke rumschrauben wollten.gtoXX hat geschrieben: ↑17.01.2020 16:091. Für mich sind zuviele Kommentare ein Zeichen für Faulheit zum sinnvollen Programmdesign
2. Ausführliche Kommentare sind eine unzuverlässige Quelle.
a). Sie kosten bei jeder Änderung sinnlose Wartungszeit
b). Es ist nie sichergestellt, dass das Kommentar die Realität widerspiegelt.
In der Theorie ja, in der Praxis wo es die eine oder andere Budgetlimitierung gibt ( Zeit oder Geld ) bei den Kunden für die ich bisher gearbeitet habe scheinbar nicht ( auch nicht auf Anfrage! )
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
DeathAndPain
Genau. Am Anfang meiner Routinen (oder vor komplizierteren LOOPs etc.) steht auch ein Textblock, der schildert, was dieser Code-Teil tun soll und was ich mir bei meinem Lösungsweg gedacht habe. Dann hat jeder, der später daran Änderungen vornehmen möchte (mich selbst eingeschlossen) die Chance, diese Überlegungen einzubeziehen und den Code im Kontext zu sehen.Aber ausführliche Kommentare sind kein Zeichen von Faulheit sondern einfach nett gegenüber denjenigen die ein Programm warten sollen. Ich freue mich immer wieder über Codestrecken wo ein "Siehe Ticket .... Folgendes Problem war damals aufgetaucht.... Anforderer war... , Folgender Lösungsansatz wurde gewählt weil... " oder auch nur Teile davon stehen.
An der Stelle liegt die Wahrheit in der Mitte, denn nicht immer trifft der Kommentar trotz bester Absichten seines Autors genau das Programmverhalten. Im Gegensatz zu Code kann man Kommentartext halt nicht testen und debuggen.Ad 2b) Wenn sie das nicht tun darf man den Verfasser des Kommentars oder desjenigen, der den Code so abgewandelt hat, dass der Kommentar nicht mehr stimmt teeren und federn.