Leider ein wirklich auch in meinen Augen ein Problem bei vielen.DeathAndPain hat geschrieben:Auch hier bin ich komplett Deiner Meinung. Tatsächlich machen es aber die wenigsten. Ich glaube, das liegt nicht nur an Faulheit, sondern auch daran, dass viele nicht in der Lage sind, ihre Gedanken schlüssig in Worte zu fassen und damit dann auch noch vollständige, korrekte deutsche Sätze zu bilden.ralf.wenzel hat geschrieben:Ich sage immer wieder Neu-Entwicklern: "Kommentieren, kommentieren, kommentieren. Und wenn du in einem Fremdprogramm etwas findest, was du nicht verstehst und du hast das irgendwann rausgefunden: KOMMENTAR REIN! - sonst stehst du in sechs Wochen wieder davor und suchst nach demselben Kram". Weil man sich das eben nicht merkt, was man vor 6 Wochen mal rausgefunden hat.
Code: Alles auswählen.
IF sy-subrc <> 0. "Fehlerhandling
Prinzipiell ja. In der Praxis ist die Definition, was rein kommen darf und was raus gehen muss nicht einfach!DeathAndPain hat geschrieben: Erschwerend kommt hinzu, dass Code Fehler enthalten kann. Ist der Fehler nicht allzu offensichtlich, dann ist es gerade bei fremdem Code nicht leicht zu beurteilen, ob man einen Bug gefunden hat oder ob der Urheber des Codes sich etwas dabei gedacht hat und man möglicherweise ein Detail der Sollfunktionalität übersieht. Wenn aber klar oben drüber steht: "Diese Methode soll zu der übergebenen Materialnummer die Lagerbestände in allen VKORGs einlesen und in der Tabelle LAGERBESTAENDE zurückliefern.", dann weiß man ganz klar, was da passieren soll und kann mit bedeutend sichererer Hand auf den Code zeigen und sagen: "Diese Stelle ist so nicht richtig!"
Code: Alles auswählen.
zcl_dbc=>require( that = 'The Monster has at least one eye'
which_is_true_if = boolc(
co_monster->md_no_of_eyes GE 1 ) ).
Irgendwo in seinem Blog, das finde ich jetzt nicht mehr wirklich wieder - aber hieraus kann man das ableiten.black_adept hat geschrieben:Ich dachte immer das sei nur deine eigene Methodik, wann/wie du Inline-Deklarationen verwendest. Aber wenn das offiziell ist, magst du mal einen Link posten, wo H. Keller das genau schreibt? Denn ich habe das so nicht in der Doku gefunden.ralf.wenzel hat geschrieben:Ich zum Beispiel verstehe eines nicht: Warum muss ich eine Variable, die ich nur sehr begrenzt verwende, oben im Coding deklarieren? Nehmen wir einen LOOP AT mara ASSIGNING <mara>. Dann muss ich <mara> oben deklarieren und wenn ich den LOOP nicht in eine eigene Methode schreibe, ist gar nicht klar, dass das nur sehr begrenzt verwendet wird. Ein LOOP AT mara ASSIGNING FIELD-SYMBOL(<mara>) zeigt mir: Ah, <mara> wird nur in diesem LOOP verwendet. Und wenn man sich die Hinweise zum Befehl ansieht, sieht man, dass Horst sehr deutlich schreibt, dass die impliziten Deklarationen genau dafür gedacht sind und nicht inflationär verwendet werden sollen.
Bei sowas hilft nur eins: Pranger aus dem Keller holen, Entwickler darin befestigen, fertig. Da hilft es, wenn man das passende "Hobby" zum Beruf hat.ewx hat geschrieben:Der Klassiker:Kein "Warum?", kein "Was habe ich erwartet?", keine Info, in welchen Fällen der Fehler durchaus vorkommen kann.Code: Alles auswählen.
IF sy-subrc <> 0. "Fehlerhandling
Ich möchte das bestreiten. CONV kann man ja ohnehin nur dort brauchen, wo eine dynamische Konvertierung möglich ist, wo man also auch eine Hilfsvariable definieren und den Inhalt dann einfach per Gleichheitszeichen zuweisen könnte. Insofern schlägt der CONV doch nur technische Brücken, ist inhaltlich aber weitgehend aussagelos, so dass man ihn problemlos überlesen kann. Da, wo CONV steht, will ich den Wert in den Klammern an, sagen wir, die Methode übergeben, ohne Ärger mit deren Parameterdefinition zu haben. Inhaltlich ist das völlig verständlich, vorausgesetzt, der Inhalt der CONV-Klammer passt von der Sache her zur Definition der Methode. Wenn nicht, dann wird CONV auch nicht funktionieren. Von daher würde ich sagen, überall dort, wo man CONV vorfindet, sollte er keine Verständnisprobleme verursachen, auch nicht mit Gartenzaun.Ja, da gebe ich dir Recht. Wenn man es _mal_ benutzt. Gerade in verketteten Aufrufen verkomplizieren die CONV-Konstrukte, da es nicht nur der Befehl CONV ist, sondern noch mindestens ein # plus Klammern.
Also ich bin durchaus schon öfter über das von mir schon zuvor geschilderte Problem gestolpert, dass mir eine Schnittstelle weggedumpt ist, weil die Domäne des übergebenen Parameters nicht identisch mit der Domäne der empfangenden Unterroutine war - und das obwohl beide Domänen technisch als Typ N mit Känge 8 definiert waren. ABAP kann da sehr kleinlich sein, und da hilft CONV wunderbar darüber hinweg.Am Häufigsten wird -wage ich einfach mal zu behaupten - CONV gebraucht, um "Text" zu übergeben (denn in den meisten anderen Fällen arbeitet man bereits mit dem richtigen Datentyp).
Sowas nervt, aber noch besser finde ich (in 5 Jahre altem Code):ewx hat geschrieben:Leider ein wirklich auch in meinen Augen ein Problem bei vielen.DeathAndPain hat geschrieben:Auch hier bin ich komplett Deiner Meinung. Tatsächlich machen es aber die wenigsten. Ich glaube, das liegt nicht nur an Faulheit, sondern auch daran, dass viele nicht in der Lage sind, ihre Gedanken schlüssig in Worte zu fassen und damit dann auch noch vollständige, korrekte deutsche Sätze zu bilden.
Der Klassiker:Code: Alles auswählen.
IF sy-subrc <> 0. "Fehlerhandling
Code: Alles auswählen.
IF sy-subrc <> 0.
" Ausprogrammieren!
ENDIF.
Doch, das muss sie sein! Wenn dem Schöpfer einer Routine nicht klar ist, was sie tun soll, dann hat er nicht genug darüber nachgedacht und sollte die Tastatur zur Seite schieben und sich überlegen, wo er mit seinem Code überhaupt hin will. Und wenn er das weiß, dann sollte er auch in der Lage sein, es zumindest in halbwegs brauchbarem Deutsch (oder von mir aus englisch) auch niederzuschreiben.ewx hat geschrieben:Prinzipiell ja. In der Praxis ist die Definition, was rein kommen darf und was raus gehen muss nicht einfach!
Ich nicht. Akademisch ist es spannend, aber was die Brauchbarkeit in der produktiven Praxis angeht, finde ich folgendes nicht nur wesentlich schneller implementierbar, sondern auch wesentlich besser verständlich (lesbar):ewx hat geschrieben:Paul Hardy spricht in seinem ziemlich genialen Buch ABAP To The Future von "Preconditions" und "Postconditions", die er auf Seite 277 wie folgt programmiert:Und die Postconditions mit ZCL_DBC=>ENSURE( THAT ...Code: Alles auswählen.
zcl_dbc=>require( that = 'The Monster has at least one eye' which_is_true_if = boolc( co_monster->md_no_of_eyes GE 1 ) ).
Die Idee finde ich super
Code: Alles auswählen.
* Wenn das Monster mindestens ein Auge hat, soll x = 1 sein, andernfalls 0.
IF co_monster->md_no_of_eyes GE 1.
x = 1.
ELSE.
x = 0.
ENDIF.
ewx hat geschrieben:die Umsetzung ist extrem aufwändig sobald man mehrere Parameter und Parameterkombinationen hat. Auch die Definition, was wirklich zulässig ist bzw. erwartet wird, kann ziemlich kompliziert sein.
Ich bin auch ein Fan sprechender Namen und laufe dabei oft genug in die in meinen Augen prähistorische 30-Zeichen-Grenze (ganz zu schweigen von dem idiotischen 8-Zeichen-Limit bei Parametern auf Report-Selektionsbildschirmen). Dennoch packe ich fast immer auch einen ergänzenden Kommentar an den Anfang der Subroutine, und wenn es nur ein trivial scheinender Satz ist. Wenn man mal geistig nicht mehr so tief in dem Code drinsteckt, ist man für solche Orientierungshilfen immer dankbar - und wenn sie einem nur bestätigen, dass man richtig verstanden hat, was da passieren soll.ewx hat geschrieben:Nichts desto Trotz: Eine kurze Beschreibung, was die Methode macht und in welchem Kontext sie verwendet wird, ist sinnvoll. GET_SPECIAL_DATA oder CHECK_STATUS ist doch ziemlich nichtssagend...
Ich verstehe halt nicht, weshalb sie die Bindung der Variablen an die Schleife nicht statisch vom Compiler durchsetzen lassen - und weshalb sie überhaupt ein Derivat ohne Schleife (mit DATA) anbieten. In meinen Augen macht das die gute Idee kaputt. Anzunehmen, dass die Mehrheit der Programmierer sich an diese Empfehlung halten wird, halte ich für geradezu naiv.ralf hat geschrieben:Irgendwo in seinem Blog, das finde ich jetzt nicht mehr wirklich wieder - aber hieraus kann man das ableiten.
Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
Daniel
Ich mag es nicht, wenn man mir widerspricht!! ;DDeathAndPain hat geschrieben:Doch, das muss sie sein! Wenn dem Schöpfer einer Routine nicht klar ist, was sie tun soll, dann hat er nicht genug darüber nachgedacht und sollte die Tastatur zur Seite schieben und sich überlegen, wo er mit seinem Code überhaupt hin will. Und wenn er das weiß, dann sollte er auch in der Lage sein, es zumindest in halbwegs brauchbarem Deutsch (oder von mir aus englisch) auch niederzuschreiben.ewx hat geschrieben:Prinzipiell ja. In der Praxis ist die Definition, was rein kommen darf und was raus gehen muss nicht einfach!
hmm. Ich habe wohl einen wichtigen Teil unterschlagen. Paul Hardy gibt in der Funktion ENSURE ein sehr leserliches Protokoll aus, wenn die Bedingung nicht zutrifft. Er schreibt dann einen Protokolleintrag, in dem er sich mittels FUBA SYSTEM_CALLSTACK den Namen der aufrufenden und der aktuellen Routine ausliest. Das Protokoll ist dann sehr sprechend:DeathAndPain hat geschrieben:ewx hat geschrieben:Paul Hardy spricht in seinem ziemlich genialen Buch ABAP To The Future von "Preconditions" und "Postconditions", die er auf Seite 277 wie folgt programmiert:Und die Postconditions mit ZCL_DBC=>ENSURE( THAT ...Code: Alles auswählen.
zcl_dbc=>require( that = 'The Monster has at least one eye' which_is_true_if = boolc( co_monster->md_no_of_eyes GE 1 ) ).
Die Idee finde ich superDeathAndPain hat geschrieben: Ich nicht. Akademisch ist es spannend, aber was die Brauchbarkeit in der produktiven Praxis angeht, finde ich folgendes nicht nur wesentlich schneller implementierbar, sondern auch wesentlich besser verständlich (lesbar):
Code: Alles auswählen.
* Wenn das Monster mindestens ein Auge hat, soll x = 1 sein, andernfalls 0. IF co_monster->md_no_of_eyes GE 1. x = 1. ELSE. x = 0. ENDIF.
Und dann ist das Ergebnis zwar toll als logische Spielerei, aber aus Sicht produktiver Arbeit schlecht verständlich!
This Diagnosis is intended for IT
Routine { ld_server_routine } of program { ld_server_program } has a contract
with routine { ld_client_routine } of program { ld_client_program }
Routine { ld_server_routine } agrees to carry out a certain task
This requires that { that }
That condition has not been fulfilled by calling routine { ld_client_routine }
and so therefore there is an error (bug) in routine { ld_client_routine } that needs to be corrected.
RAISE EXCEPTION TYPE zcx_violated_precondition
EXPORTING
md_condition = that
mo_error_log = lo_log.
Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
Daniel
Folgende Benutzer bedankten sich beim Autor ralf.wenzel für den Beitrag:
DeathAndPain
@DeathAndPain: Ist mir leider zwischendurch entfallen dass ich hier noch was zu fragen wollte: Einerseits plädierst du wie Horst Keller in Ralfs Link für Datendeklarationen am Anfang der Modularisierungseinheit. Andererseits erinnere ich mich, dass du die räumliche Trennung von Klassendefinitionen und Implementierung beklagst. Ist das kein Widerspruch?DeathAndPain hat geschrieben:Womit ich mich allerdings weiterhin nicht anfreunden kann, sind Inline-Deklarationen vom Typ NEW. Ich bin immer noch der Meinung, dass Datendeklarationen an den Anfang des Codes gehören und nicht mittenrein. Man könnte auch mittendrin einen DATA-Befehl bringen und würde sich damit (IMHO zu recht) eine Menge Schelte einfangen, obwohl es syntaktisch korrekt ist. NEW macht den Code spürbar schlechter lesbar für den Gewinn ein klitzekleinen Bisschens weniger Schreibarbeit.
Mein Lieblingsbeispiel ist die Kombination von CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG, dessen Rückgabetabelle Dateiname vom Typ FILENAME = CHAR1024 enthält, wohingegen dann das üblicherweise folgende CL_GUI_FRONTEND_SERVICES=>GUI_UPLOAD als Dateinamen gerne den Typ String hätte.ralf.wenzel hat geschrieben:Noch ein Wort zu CONV: Ich sehe das nicht, dass ich jeden Datentyp in einem Programm vorhalten muss, den eine aufgerufene Routine in der Schnittstelle hat. Schon deshalb, weil zum Teil verschiedene Funktionsbausteine einer Funktionsgruppe mal den einen, mal den anderen Typen verlangt, auch wenn mir gerade kein Beispiel dazu einfällt (mich hat sowas schon mehrfach geärgert).
Darin kann ich keinen Widerspruch erkennen. Das beste Gegenbeispiel ist die FORM-Routine. Am Anfang stehen die Parameter, dann deklariert man per DATA alles, was man darin so braucht, und dann arbeitet man damit. Stattdessen aus der FORM zwei getrennte Blöcke zu machen, von denen der eine die Parameter und der andere die lokalen Variablen und den Code enthält (wie es bei lokalen Methoden der Fall ist), würde das Gebilde nur aufblähen. Zudem hätte man damit eben nicht mehr alle Deklarationen an einem Ort: Die lokalen Variablen stehen an einer Stelle, die Parameterdefinitionen (und nur um die geht es ja dabei) an einer völlig anderen. Sei ehrlich: Wenn Du mit Funktionsbausteinen arbeitest, schaltest Du dann immer auf den Parameterreiter (von denen es zudem noch mehrere gibt (Import/Export/Tables/Exceptions)) um, um rasch einen Parameter nachzusehen, oder bist Du nicht auch dankbar, dass das System Dir automatisiert die komplette Parameterliste als Kommentar an den Anfang des Quelltextes packt und diese bei Parameteränderungen auch stets aktualisiert? Bei globalen Klassen hat man dafür die Parameterdarstellung oben im Fenster (und das auch nur in der SE24, nicht in Eclipse). Bei lokalen Klassen jedoch stehste da.black_adept hat geschrieben:@DeathAndPain: Ist mir leider zwischendurch entfallen dass ich hier noch was zu fragen wollte: Einerseits plädierst du wie Horst Keller in Ralfs Link für Datendeklarationen am Anfang der Modularisierungseinheit. Andererseits erinnere ich mich, dass du die räumliche Trennung von Klassendefinitionen und Implementierung beklagst. Ist das kein Widerspruch?
Code: Alles auswählen.
if me->structure is initial.
me->structure = get_structure( ).
endif.