Au, bitter, jetzt fangen wir aber eine Diskussion an. Ein Attribut ist von Haus aus semantisch etwas vollkommen anderes (spezifischeres) als eine Variable.DeathAndPain hat geschrieben: ↑04.09.2024 11:22die globalen Variablen der Funktionsgruppe sind die Attribute. Womit einmal mehr das deutlich wird, was ich immer wieder gerne betone: Attribute sind nichts anderes als globale Variablen mit all ihren Nachteilen
Richtig. Weil das Objekt einen Zustand hat. Man kann auch zustandslos programmieren, aber damit nimmt man ABAP OO eine wichtige Funktionalität.DeathAndPain hat geschrieben: ↑04.09.2024 11:22Durch die Attribute ist das Verhalten der Methoden nicht mehr durch das definiert, was man den Methoden über ihre Schnittstelle übergibt, und eine Methode kann sich mal so und mal völlig anders verhalten, je nachdem, was eine völlig andere Methode irgendwann früher mal irgendwo in einem Attribut abgelegt hat.
Naja, von den Dynpros wollte man ja eigentlich mal runter bei der SAP, aber das ist ein anderes Thema. Tatsächlich nutze ich Funktionsgruppen nur noch für Dynpros.DeathAndPain hat geschrieben: ↑04.09.2024 11:22Vom Leistungsumfang sind Funktionsgruppen und statische Klassen ohne Vererbung also gleich. Wenn man das Detail hinzunimmt, dass Funktionsgruppen eigene Dynpros haben und RFC-tauglich sein können, können sie in bestimmten Hinsichten sogar ein bisschen mehr.
Folgende Benutzer bedankten sich beim Autor ralf.wenzel für den Beitrag:
tar
Folgende Benutzer bedankten sich beim Autor tar für den Beitrag (Insgesamt 2):
ralf.wenzel • black_adept
Gerade in dem von D&P benannten Ansatz Methoden abstrakter Klassen ( warum eigentlich abstrakt? Die kann auch ganz normal sein solange du nur statische Methode einbaust ) genau so zu verwenden wie Funktionsbausteine ist diese Analogie absolut korrekt.ralf.wenzel hat geschrieben: ↑04.09.2024 11:47Au, bitter, jetzt fangen wir aber eine Diskussion an. Ein Attribut ist von Haus aus semantisch etwas vollkommen anderes (spezifischeres) als eine Variable.DeathAndPain hat geschrieben: ↑04.09.2024 11:22die globalen Variablen der Funktionsgruppe sind die Attribute. Womit einmal mehr das deutlich wird, was ich immer wieder gerne betone: Attribute sind nichts anderes als globale Variablen mit all ihren Nachteilen
Ralf
Hast du nicht letztens irgendwo geschrieben, dass du Leute die noch IF statt SWITCH schreiben ( so wie ich ) als rückständig ansiehst und dass dir egal ist, ob die Mitarbeiter deine Programme warten können weil sie eventuell noch nicht auf deinem Wissensstand sind. Irgendwie hatte ich so das Gefühl, dass du dich fast diebisch freust, wenn dem so ist.DeathAndPain hat geschrieben: ↑03.09.2024 21:04Ich zum Beispiel habe zwar Vererbung und Objekte verstanden, brauche aber in der Praxis praktisch nur abstrakte und finale Klassen, weil sich mir in meiner Praxis keine so komplexen Probleme stellen, dass Instanziierungen und Vererbungen mir Vorteile brächten.
Nein, es gibt auch Zangen (Interfaces) und viele andere Werkzeuge. Das Wort "Vererbung" habe ich in diesem Thread nicht verwendet und ich benutze sie auch so oft gar nicht.black_adept hat geschrieben: ↑04.09.2024 17:16Hier verstehe ich z.B. Ralf nicht - ich habe immer das Gefühl als ob bei ihm DesignPatterns und Vererbung der Hammer sind und er dann all seine Probleme als Nägel ansieht. Ich gehe davon aus dass sich in seiner Werkzeugbank auch Zangen und Schraubendreher befinden, die inzwischen aber Spinnenweben ansetzen, weil er sich weigert über den OO-Tellerrand hinauszuschauen.
Wie von black_adept schon eingewendet habe ich explizit "abstrakte Klassen" gesagt, also Klassen, die nicht instanziierbar sind. Bei solchen Klassen hast Du nicht mehrere Objekte für mehrere Belege (oder was auch immer). Natürlich kann man mit Instanziierung von Objekten Sachen machen, die über Funktionsgruppen hinausgehen. Das habe ich nie bestritten. Allerdings habe ich auf den Umstand hingewiesen, dass der Löwenanteil der Klassen, die ich von anderen Entwicklern zu sehen bekomme, nur genau einmal instanziiert wird. Dann aber ist die Instanziierung nutzlos und legt nur eine überflüssige Abstraktionsebene drüber, die die Performance senkt und das Programm schlechter lesbar macht.Ralf hat geschrieben:Ein Attribut ist von Haus aus semantisch etwas vollkommen anderes (spezifischeres) als eine Variable.
Beispiel: Ein Objekt bildet einen Beleg ab. Dann ist der Zustand des Beleges (gebucht, vorerfasst, storniert) durch Attribute festgelegt. Unter der Gruppe der in der Klasse verwendeten Variablen ist die Untergruppe der Attribute die, die den Zustand des Objektes beschreibt.
Das stimmt nicht so ganz. Von der Lesbarkeit her sind PERFORM-Aufrufe einfach schrecklich, zumal sie bei ihren Parametern keine Formeln und keine Inline-Deklarationen erlauben und nicht funktional nutzbar sind. Allein dafür lohnt es sich schon, stattdessen Methoden zu verwenden. Die Aufteilung in DEFINITION und IMPLEMENTATION-Block halte ich für akademisch und nutzlos; das ist nur Schreibarbeit und erschwert die Lesbarkeit des Programms. Doch sogar diesen Preis bin ich für die um Längen übersichtlicheren Methodenaufrufe bereit zu zahlen. Wobei ich mir aber die Arbeit mache, die Parametrisierung meiner Methoden als Kommentar nochmal dorthin zu übertragen, wo sie meiner Meinung nach hingehört: an den Anfang der IMPLEMENTATION. Des Problems, dass solch Kommentar sich (anders als bei Funktionsbausteinen) nicht automatisch aktualisiert, wenn ich einen Parameter ändere, bin ich mir bewusst.Ralf hat geschrieben:Wenn man natürlich nur nicht nicht-instanziierbaren Klassen arbeitet, dann kann man sich das auch schenken, da hast du recht. Aber das ist ja nicht das, was man unter OO versteht. Objektorientierte Programmierung ohne Objekte ist irgendwie sinnfrei, das ist wie Autofahren ohne Auto.
Wie gesagt, wenn die Zustände sinnvoll genutzt und so gut dokumentiert werden, dass auch ein anderer Entwickler nachvollziehen kann, wofür genau die Attribute stehen und wo ihre Werte herkommen (können) und wann sie gesetzt werden, dann habe ich da keine Einwände. In der von mir erlebten Praxis sehe ich aber fast nur Ein-Objekt-Klassen mit als globalen Variablen (im negativsten Sinne des Wortes) verwendeten "Attributen".Ralf hat geschrieben:Man kann auch zustandslos programmieren, aber damit nimmt man ABAP OO eine wichtige Funktionalität.
Das ist ein berechtigter Einwand, ebenso wie der Umstand, dass es keine lokalen Funktionsgruppen gibt. Aber ich bitte, sich zu erinnern, was ich eigentlich sagen wollte. Ich habe nie postuliert, dass man anstelle von Klassen besser Funktionsgruppen verwenden solle, sondern wollte nur auf die auffallenden Parallelen zwischen Funktionsgruppen und abstrakten Klassen aufmerksam machen. Das wirft ein Schlaglicht auf das Konzept der "Attribute". Für mich sind "Attribute" nichts anderes als schöngeredete globale Variablen. Die Nachteile sind nämlich genau dieselben: Sie sind nicht Bestandteil des Parameterinterfaces der Methoden, und wenn man das Programm nicht selber geschrieben hat, ist es sauschwer nachzuvollziehen, wo ihre Werte herkommen und wofür sie irgendwann später im Code noch alles verwendet werden (welche Auswirkungen eine Änderung also hat). Theoretisch müsste man das superakkurat dokumentieren. Praktisch habe ich in meinem ganzen Leben noch nicht ein OO-Programm gesehen, dessen Ersteller das gemacht hätte, erst recht nicht dort, wo man solch Doku unter allen Umständen wiederfindet, nämlich direkt in der Klassen- oder Methodendokumentation.Ralf hat geschrieben: Da gehen die Unterschiede doch schon los. Außerdem sind die Namen von Funktionsbausteinen global, das heißt: Ich kann den Funktionsbaustein "SAVE" nicht in drei verschiedenen Funktionsgruppen anzulegen (bei Klassen und Methoden geht das), weil Methodennamen nicht global definiert sind.
Klar kann man sich auch bei herkömmlichen Klassen entscheiden, keine instanziierten Methoden einzubauen. Wenn ich aber das Postulat aufstelle, dass Funktionsgruppen weitestgehend äquivalent zu Klassen sind, dann ergibt es Sinn, dies auf abstrakte Klassen einzuschränken, denn Funktionsgruppen sind ja auch nicht instanziierbar. Man könnte auch "final" sagen, aber das bringt nicht viel, denn auch finale Klassen können von oben erben. Deshalb habe ich lieber explizit "ohne Vererbung" gesagt.black_adept hat geschrieben:( warum eigentlich abstrakt? Die kann auch ganz normal sein solange du nur statische Methode einbaust )
So weit würde ich nicht gehen. Es geht mir nicht darum, Kollegen zu verarschen, und Code sollte gut lesbar und verständlich sein. Nur bin ich halt nicht bereit, Rücksicht darauf zu nehmen, wenn jemand immer noch nicht Befehle kennt, die vor einer ganzen Reihe von Jahren (mit 7.40) eingeführt worden sind. Wenn man auf solch Befehl stößt, kann man ja auch jederzeit die F1-Hilfe aufrufen und sich durchlesen, wie der Befehl funktioniert. Das finde ich durchaus zumutbar.black_adept hat geschrieben:Hast du nicht letztens irgendwo geschrieben, dass du Leute die noch IF statt SWITCH schreiben ( so wie ich ) als rückständig ansiehst und dass dir egal ist, ob die Mitarbeiter deine Programme warten können weil sie eventuell noch nicht auf deinem Wissensstand sind. Irgendwie hatte ich so das Gefühl, dass du dich fast diebisch freust, wenn dem so ist.
Ich hatte vergessen, wieviel Spaß diese Diskussionen machen - merke ich gerade 😉
Das ist falsch. Ein Objekt ist nicht deshalb nutzlos, weil es das einzige ist.DeathAndPain hat geschrieben: ↑05.09.2024 10:41der Löwenanteil der Klassen, die ich von anderen Entwicklern zu sehen bekomme, nur genau einmal instanziiert wird. Dann aber ist die Instanziierung nutzlos
Sorry, aber das ist falsch. Das trägt dem Umstand Rechnung, dass die Methodenschnittstelle ein Teil der Klassenschnittstelle sind. Eine Funktionsgruppe hat keine Schnittstelle.DeathAndPain hat geschrieben: ↑05.09.2024 10:41Die Aufteilung in DEFINITION und IMPLEMENTATION-Block halte ich für akademisch und nutzlos; das ist nur Schreibarbeit und erschwert die Lesbarkeit des Programms.
DeathAndPain hat geschrieben: ↑05.09.2024 10:41Wobei ich mir aber die Arbeit mache, die Parametrisierung meiner Methoden als Kommentar nochmal dorthin zu übertragen, wo sie meiner Meinung nach hingehört: an den Anfang der IMPLEMENTATION.
Und damit erhöhst du den Wartungsaufwand, obwohl die Methodenschnittstelle nur ein Mouseover entfernt ist und in der SAPGUI sogar oben eingeblendet wird.DeathAndPain hat geschrieben: ↑05.09.2024 10:41Des Problems, dass solch Kommentar sich (anders als bei Funktionsbausteinen) nicht automatisch aktualisiert, wenn ich einen Parameter ändere, bin ich mir bewusst.
Wenn jemand schlecht programmiert, ist das kein Argument gegen das Paradigma. Ich kenne deutlich mehr schlecht programmierte prozedurale Programme als OO-Anwendungen. Das fängt schon damit an, dass ein Attribut sich durch seinen Namen identifizieren sollte. Ansonsten gibt es den Verwendungsnachweis.DeathAndPain hat geschrieben: ↑05.09.2024 10:41Wie gesagt, wenn die Zustände sinnvoll genutzt und so gut dokumentiert werden, dass auch ein anderer Entwickler nachvollziehen kann, wofür genau die Attribute stehen und wo ihre Werte herkommen (können) und wann sie gesetzt werden, dann habe ich da keine Einwände. In der von mir erlebten Praxis sehe ich aber fast nur Ein-Objekt-Klassen mit als globalen Variablen (im negativsten Sinne des Wortes) verwendeten "Attributen".
Ich bleibe dabei: Ein Attribut ist eine im semantischen Kontext enger definierte Variable, weil sie einen Zustand beschreibt (was eine Variable nicht zwingend tut).DeathAndPain hat geschrieben: ↑05.09.2024 10:41Das wirft ein Schlaglicht auf das Konzept der "Attribute". Für mich sind "Attribute" nichts anderes als schöngeredete globale Variablen.
Das ist bei schlecht geschriebenem Coding immer so. Das liegt im Ermessen des Entwicklers, das so eindeutig zu machen, dass man sich eben nicht einen Wolf sucht.DeathAndPain hat geschrieben: ↑05.09.2024 10:41Die Nachteile sind nämlich genau dieselben: Sie sind nicht Bestandteil des Parameterinterfaces der Methoden, und wenn man das Programm nicht selber geschrieben hat, ist es sauschwer nachzuvollziehen, wo ihre Werte herkommen und wofür sie irgendwann später im Code noch alles verwendet werden (welche Auswirkungen eine Änderung also hat).
CASE testet gegen Fixwerte, IF, ELSEIF, ELSE gegen boolsche Ausdrücke. Das ist wesentlich unterschiedlich und vergleichbar eher mit dem Unterschied zwischen SWITCH und COND. Alles hat seine Daseinsberechtigung und ich verwendet tatsächlich jeden dieser 4 Befehle immer dort wo es mir am sinnvollsten erscheint. Aber wenn ich die gleichberechtigte Wahl zwischen IF und SWITCH habe entscheide ich mich halt am häufigsten für IF. Und ich wette auch du verwendest IF noch bei IF <bedingung> {codeblock} ENDIFDeathAndPain hat geschrieben: ↑05.09.2024 10:41Und "IF" statt "SWITCH" finde ich schon insofern verwerflich, als da schon unter Release 3.1 "CASE" der bessere Ansatz gewesen wäre.
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
ralf.wenzel
Hoffentlich zieht Du auch die entsprechenden Konsequenzen. 😁Ralf hat geschrieben:Ich hatte vergessen, wieviel Spaß diese Diskussionen machen
Wikipedia definiert eine Dependency Injection als "Entwurfsmuster, welches die Abhängigkeiten eines Objekts zur Laufzeit reglementiert". Es ist absurd, im Rahmen einer abstrakten Klasse von "Abhängigkeiten eines Objektes" zu sprechen, da abstrakte Klassen keine Objekte haben.Ralf hat geschrieben:Mach doch mal eine dependency injektion bei einer Funktionsgruppe.
Mit einer abstrakten Klasse sollte das alles kein Problem sein. Dass dies syntaktisch bei Funktionsgruppen nicht vorgesehen ist, ändert nichts an den konzeptuellen Parallelen zwischen Funktionsgruppen und abstrakten Klassen. Wie gesagt, ich habe nie gefordert, Funktionsgruppen anstelle abstrakter Klassen zu verwenden.Ralf hat geschrieben:Schreibe einen Wrapper mit einer Funktionsgruppe. Nutze verschiedene Interfaces für eine Funktionsgruppe. Schreibe mal einen Unit-Test mit einer Funktionsgruppe
Das halte ich für einen komplett legitimen Einsatzzweck echter Objekte. Ich habe ja auch nie gesagt, dass es solche Einsatzzwecke nicht gibt, sondern nur, dass man sie in den Programmen, die ich typischerweise zu sehen bekomme, praktisch nicht zu Gesicht bekommt.Ralf hat geschrieben:Der eigentliche Vorteil eines Objektes ist aber, dass ich eine Einheit von Funktion und Variablen übergeben kann. Ich hatte das mal ausführlich anhand eines Buttons beschrieben: Ich habe den Button, sein Aussehen, seinen Namen UND seine Funktion in einem Objekt gespeichert und kann dieses Objekt geschlossen z. B. an einen ALV übergeben; und so dieselben (!) Buttons mit denselben (!) Funktionen in verschiedenen Programmen nutzen. Und dann sage ich "wenn auf einen Button geklickt wird, dann führe bitte die Methode RUN eben dieses Objektes (Buttons) aus, auf das gerade geklickt wurde. Der Aufrufer muss sich um nichts kümmern.
Wenn Du ein OO-Programm schreibst, dann ergibt sich daraus praktisch zwingend das Vorhandensein mindestens einer lokalen Klasse. Die ist es, was Dein Programm ausmacht. Und jegliche lokalen Klassen und Methoden haben keinen Wiederverwendungswert für andere Programme.Ralf hat geschrieben:Das alles geht mit einer Funktionsgruppe und auch mit einer statischen Klasse nicht. Und dann verwendet man Objekte, auch wenn man nur eines davon hat.
Das meinte ich mit "akademisch". Was Du sagst, ist formal richtig, für die Praxis aber ohne Bedeutung. Interfaces benutzt Du für Deine Methodenparametrisierung. Auch der Implementation-Block der Klasse ist durch CLASS...ENDCLASS eingeschlossen. Es wäre völlig ausreichend, nur diesen Block zu haben, ihn in PUBLIC/PROTECTED/PRIVATE-Sektionen zu untgergliedern und am Beginn jeder Methode deren Parameter mit anzugeben. Dabei könnte man dann auch gleich die bescheuerte Regel beseitigen, dass Klassen bzw. ihre Methoden erst dann gefunden werden, wenn der Compiler sie erreicht hat. Das kann jede antike Formroutine besser; bei der ist es egal, wo im Code sie steht. Dann würde man auch die DEFINITION DEFERRED-Krücke nicht mehr brauchen.Ralf hat geschrieben:Sorry, aber das ist falsch. Das trägt dem Umstand Rechnung, dass die Methodenschnittstelle ein Teil der Klassenschnittstelle sind. Eine Funktionsgruppe hat keine Schnittstelle.DeathAndPain hat geschrieben:Die Aufteilung in DEFINITION und IMPLEMENTATION-Block halte ich für akademisch und nutzlos; das ist nur Schreibarbeit und erschwert die Lesbarkeit des Programms.
Ja, wie gesagt, den Preis des erhöhten Wartungsaufwands zahle ich für die bessere Lesbarkeit. Bei meinem Eclipse kommt nichts, wenn ich den Mauscursor über einen Methodennamen halte, da muss ich während des Mouseovers schon F2 drücken. Im SAPGui wird bei lokalen Klassen überhaupt nichts oben eingeblendet (und auch globale Klassen programmiere ich nicht in der SE24, weil mir ein schmaler Fensterschlitz für den Code nicht ausreicht. Diese Fenstergestaltung ist eine erkennbare Konzession an die fehlende Schnittstellenangabe in der Implementierung. Aber da ist Eclipse um Längen komfortabler. Da ist sogar die SE37 mit ihrem automatisch aktualisierten Kommentartext am Codebeginn komfortabler.).Ralf hat geschrieben:Und damit erhöhst du den Wartungsaufwand, obwohl die Methodenschnittstelle nur ein Mouseover entfernt ist und in der SAPGUI sogar oben eingeblendet wird.
Wenn die Praxis jedoch zeigt, dass alle es tun (die SAP eingeschlossen), dann ist das ein sehr gewichtiges Argument dagegen.Ralf hat geschrieben:Wenn jemand schlecht programmiert, ist das kein Argument gegen das Paradigma.
Ich nicht. Vor allem aber kann man sich bei schlecht programmierten prozeduralen Programmen meist noch erheblich besser mit dem Debugger retten und sich die Funktionsweise des Programms erarbeiten als bei einem mies programmierten OO-Programm. Im Debugger ist eine Struktur oder interne Tabelle allemal leichter zu verstehen als ein Objekt, das man nicht kennt.Ralf hat geschrieben:Ich kenne deutlich mehr schlecht programmierte prozedurale Programme als OO-Anwendungen.
Und ich bleibe auch dabei: Dein Standpunkt hat hohen universitären Wert, ist vom praktischen Blickwinkel aus freilich anders zu bewerten. 😉Ralf hat geschrieben:Ich bleibe dabei: Ein Attribut ist eine im semantischen Kontext enger definierte Variable, weil sie einen Zustand beschreibt (was eine Variable nicht zwingend tut)
Und die Rede war von SWITCH vs IF. Da SWITCH auch nur gegen Fixwerte testet, ist CASE eine hundertprozentige Alternative. Mehr habe ich nicht gesagt. Dass es auch COND gibt und dabei andere Verhältnisse vorliegen, ist unbestritten.black_adept hat geschrieben:CASE testet gegen Fixwerte, IF, ELSEIF, ELSE gegen boolsche Ausdrücke.
Richtig, darum braucht man Objekte, auch wenn es einzelne sind.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Es ist absurd, im Rahmen einer abstrakten Klasse von "Abhängigkeiten eines Objektes" zu sprechen, da abstrakte Klassen keine Objekte haben.
Du hast bestritten, dass es sinnfrei ist, ein einzelnes Objekt zu verwenden. Aber gerade wenn ich die Einheit von Coding und Daten übergeben will, kommt man an einem Objekt nicht vorbei.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Das halte ich für einen komplett legitimen Einsatzzweck echter Objekte. Ich habe ja auch nie gesagt, dass es solche Einsatzzwecke nicht gibt, sondern nur, dass man sie in den Programmen, die ich typischerweise zu sehen bekomme, praktisch nicht zu Gesicht bekommt.Ralf hat geschrieben:Der eigentliche Vorteil eines Objektes ist aber, dass ich eine Einheit von Funktion und Variablen übergeben kann.
Öhm - nein. Ich kann in einem Programm auch lauter globale Klassen aufrufen. Das ist bei mir in der Tat die Regel.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Wenn Du ein OO-Programm schreibst, dann ergibt sich daraus praktisch zwingend das Vorhandensein mindestens einer lokalen Klasse.
Also, ich schreibe fast alles ins DDIC, weil ich immer wieder gestaunt habe, wie viel sich wiederverwenden lässt, wenn man es nur abstrakt genug hält.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Klar, was sich wiederverwenden lässt, gehört ins DDIC. Im Regelfall wirst Du aber kaum ein Programm schreiben können, das sich ausschließlich aus wiederverwendbaren Algorithmen speist und daher ganz ohne lokale Unterroutinen auskommt.
Wenn man davon ausgeht, dass ein Objekt keine Vorteile bietet, muss man das so sehen, ja.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Im START-OF-SELECTION ein Objekt zu erzeugen und dann (für den Rest des Programms) in dessen MAIN-Methode abzuspringen, finde ich idiotisch. Da reicht locker eine statische Klasse mit einem Absprung LC=>MAIN( ). Kürzer und besser lesbar.
Das bestreite ich. Einen Haufen Laufzeitfehler erzeugt man damit, indem man ein typfalsches Feld an einen Funktionsbaustein übergeben will. Weil kaum einer die "tiefe Prüfung" durchführt. Bei einer Methode fällt das sofort bei der einfachen Syntaxprüfung auf, ohne deren Korrektheit man gar nicht aktivieren kann.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Das meinte ich mit "akademisch". Was Du sagst, ist formal richtig, für die Praxis aber ohne Bedeutung.
Nein, Interfaces benutze ich, um einer Klasse bestimmte Eigenschaften zu geben.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Interfaces benutzt Du für Deine Methodenparametrisierung.
Das würde dazu führen, dass die Syntaxprüfung die Klasse "aufschnüren" müsste wie das bei Funktionsgruppen der Fall ist. Weil man aber einen kleinen Block für die Definition hat und einen (deutlich größeren) für die Implementierung macht es einen erheblichen Vorteil, nur den kleinen Block bei der Prüfung einzubeziehen.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Auch der Implementation-Block der Klasse ist durch CLASS...ENDCLASS eingeschlossen. Es wäre völlig ausreichend, nur diesen Block zu haben, ihn in PUBLIC/PROTECTED/PRIVATE-Sektionen zu untgergliedern und am Beginn jeder Methode deren Parameter mit anzugeben.
Naja, das mit der Lesbarkeit ist so eine Sache. Wenn ich eine lange Methode habe (und die lassen sich teilweise nicht verhindern), muss ich ständig scrollen, weil diese Kommentare nicht im Blick sind. Das Problem hab ich bei Funktionsbausteinen oft. Da ist es mir doch lieber, ich kann die per F2 oder Mouseover nachschlagen.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Ja, wie gesagt, den Preis des erhöhten Wartungsaufwands zahle ich für die bessere Lesbarkeit.
Der "Schlitz" ist ein Schlitz, weil oben genau das steht, was du forderst: Die Methodensignatur.DeathAndPain hat geschrieben: ↑05.09.2024 12:56globale Klassen programmiere ich nicht in der SE24, weil mir ein schmaler Fensterschlitz für den Code nicht ausreicht. Diese Fenstergestaltung ist eine erkennbare Konzession an die fehlende Schnittstellenangabe in der Implementierung. Aber da ist Eclipse um Längen komfortabler.
Das sehe ich komplett anders. Ich möchte mich nicht einschränken lassen von Leuten, die das Prinzip nicht verstanden haben.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Wenn die Praxis jedoch zeigt, dass alle es tun (die SAP eingeschlossen), dann ist das ein sehr gewichtiges Argument dagegen.
Also, das kann ich fast gar nicht glauben, weil in JEDEM System in dem ich bisher gearbeitet habe, ist die Zahl prozeduraler Programm DEUTLICH höher als die Zahl objektorientierter Codingeinheiten. Da wäre es erstaunlich, wenn ein deutlich höherer Prozentsatz prozeduraler Programme signifikant ordentlicher programmiert wären als ihre OO-Pendants.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Ich nicht. Vor allem aber kann man sich bei schlecht programmierten prozeduralen Programmen meist noch erheblich besser mit dem Debugger retten und sich die Funktionsweise des Programms erarbeiten als bei einem mies programmierten OO-Programm. Im Debugger ist eine Struktur oder interne Tabelle allemal leichter zu verstehen als ein Objekt, das man nicht kennt.Ralf hat geschrieben:Ich kenne deutlich mehr schlecht programmierte prozedurale Programme als OO-Anwendungen.
Der Grund für die Trennung in Definition und Implementierung ist, dass es dadurch möglich ist das Coding in vererbten Methoden auszutauschen.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Das meinte ich mit "akademisch". Was Du sagst, ist formal richtig, für die Praxis aber ohne Bedeutung. Interfaces benutzt Du für Deine Methodenparametrisierung. Auch der Implementation-Block der Klasse ist durch CLASS...ENDCLASS eingeschlossen. Es wäre völlig ausreichend, nur diesen Block zu haben, ihn in PUBLIC/PROTECTED/PRIVATE-Sektionen zu untgergliedern und am Beginn jeder Methode deren Parameter mit anzugeben. Dabei könnte man dann auch gleich die bescheuerte Regel beseitigen, dass Klassen bzw. ihre Methoden erst dann gefunden werden, wenn der Compiler sie erreicht hat. Das kann jede antike Formroutine besser; bei der ist es egal, wo im Code sie steht. Dann würde man auch die DEFINITION DEFERRED-Krücke nicht mehr brauchen.
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
ralf.wenzel
Wozu braucht man im Report eine zusätzliche Main-Methode? Das ist an sich dubios.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Im START-OF-SELECTION ein Objekt zu erzeugen und dann (für den Rest des Programms) in dessen MAIN-Methode abzuspringen, finde ich idiotisch. Da reicht locker eine statische Klasse mit einem Absprung LC=>MAIN( ). Kürzer und besser lesbar.
Code: Alles auswählen.
parameters p_user type xubname.
start-of-selection.
data(user) = new lcl_user( p_user ).
data(used_user_passwords) = user->get_password_history( ).
" ... anzeigen, per Mail senden, Log schreiben, whatever ...
Das ist beides gruselig. Unverständliches kommt aber eher daher, dass man eine schlechte Codebasis über Jahre oder Jahrzehnte immer weiter ergänzt, statt zwischendurch mal grundlegend überarbeitet hat.DeathAndPain hat geschrieben: ↑05.09.2024 12:56Vor allem aber kann man sich bei schlecht programmierten prozeduralen Programmen meist noch erheblich besser mit dem Debugger retten und sich die Funktionsweise des Programms erarbeiten als bei einem mies programmierten OO-Programm. Im Debugger ist eine Struktur oder interne Tabelle allemal leichter zu verstehen als ein Objekt, das man nicht kennt.
Folgende Benutzer bedankten sich beim Autor tar für den Beitrag:
ralf.wenzel
Folgende Benutzer bedankten sich beim Autor ralf.wenzel für den Beitrag:
tar
Warum soll das eine abstrakte Klasse nicht können? Die hat auch (statische) Attribute und Methoden, in denen Du das Gleiche unterbringen kannst wie in den Attributen einer Objektinstanz. Aber halt nur einmal. Was völlig ausreicht, wenn "nur einmal" genau das ist, was Du brauchst. Man kann eine abstrakte Klasse betrachten wie eine herkömmliche Klasse mit einer impliziten Instanz. Das Einzige, was Du bei einer statischen Klasse naturgemäß nicht hast, ist ein impliziter Konstruktor. Aber den kannst Du ja bei Bedarf leicht durch eine factoryartige Initialisierungsmethode ersetzen.Ralf hat geschrieben:Du hast bestritten, dass es sinnfrei ist, ein einzelnes Objekt zu verwenden. Aber gerade wenn ich die Einheit von Coding und Daten übergeben will, kommt man an einem Objekt nicht vorbei.
Ich schreibe auch öfter mal eine globale Klasse, weil ich mir denke "das kann man bestimmt noch mal wieder gebrauchen". Aber wenn ich es ein halbes Jahr später wirklich mal wieder brauche, dann habe ich in vielen Fällen längst vergessen, dass ich solch Klasse mal geschrieben habe. Ich staune immer wieder, was für (von mir selber stammende) nützliche Klassen und Methoden ich immer mal wieder finde und denke mir dann: "Das hättest Du in Programm xy gut gebrauchen können, wenn Du Dich daran erinnert hättest."Ralf hat geschrieben:Also, ich schreibe fast alles ins DDIC, weil ich immer wieder gestaunt habe, wie viel sich wiederverwenden lässt, wenn man es nur abstrakt genug hält.
Das liegt an der zweifelhaften Entscheidung der SAP, dass bei Funktionsbausteinaufrufen der Name letztlich immer indirekt übergeben wird. Deswegen muss man ihn ja auch immer in Hochkommata stellen. Klar, bei einer indirekten Namensangabe kann keine Syntaxprüfung zum Compile-Zeitpunkt erfolgen. Mit der Funktionalität von Funktionsbausteinen hat das aber nicht viel zu tun.Ralf hat geschrieben:Das bestreite ich. Einen Haufen Laufzeitfehler erzeugt man damit, indem man ein typfalsches Feld an einen Funktionsbaustein übergeben will. Weil kaum einer die "tiefe Prüfung" durchführt. Bei einer Methode fällt das sofort bei der einfachen Syntaxprüfung auf, ohne deren Korrektheit man gar nicht aktivieren kann.
Bitte nenne mal ein praktisches Beispiel, damit die Sache konkret wird.Ralf hat geschrieben:Nein, Interfaces benutze ich, um einer Klasse bestimmte Eigenschaften zu geben.
Bei Rechnern im unteren dreistelligen Megahertzbereich, wie sie üblich waren, als es Funktionsgruppen schon gegeben hat, mag das - möglicherweise - mal ein Thema gewesen sein, aber heutige Maschinen lachen sich über die Rechenlast für solch Syntaxprüfung doch schlapp. Eigentlich sind Syntaxprüfungen aber noch nie etwas gewesen, was nennenswert Rechenaufwand erfordert. Deswegen macht Eclipse sie ja sogar in Echtzeit, während Du tippst. Da musst Du noch nicht mal zu kompilieren versuchen.Ralf hat geschrieben:Das würde dazu führen, dass die Syntaxprüfung die Klasse "aufschnüren" müsste wie das bei Funktionsgruppen der Fall ist. Weil man aber einen kleinen Block für die Definition hat und einen (deutlich größeren) für die Implementierung macht es einen erheblichen Vorteil, nur den kleinen Block bei der Prüfung einzubeziehen.
So richtig habe ich nicht verstanden, welche Syntaxprüfung Du hier genau meinst. Die wenn man die Funktionsgruppe kompiliert? Da wird das Rahmenprogramm kompiliert und auch alle darin enthaltenen Includes geprüft. Oder wenn man sie von irgendwoher aufruft? Da hast Du den indirekten Aufruf, der eine Prüfung zur Compile-Zeit unmöglich macht. Auch wenn die SAP das selber nicht ganz konsequent einhält, wenn man sich überlegt, dass man beim Tippen (sowohl in Eclipse als auch in der SE38) mit Strg+Space eine Eingabeschablone für den FB erhalten kann, nachdem man CALL FUNCTION 'Bausteinname' getippt hat.Ralf hat geschrieben:Darum wird die "tiefe Prüfung" bei der Syntaxprüfung von Funktionsgruppen nicht durchlaufen - weil es zu lange dauert, das ganze Paket aufzuschnüren und darin herumzusuchen.
Ich hingegen empfinde vor allem Medienbrüche als schlimm. Wenn ich rasch eine Parametrisierung nachschauen muss, während ich in der Methode tippe, dann kann ich rasch zweimal die Bild-hoch-Taste drücken, gucken, dann wieder zweimal Bild runter und weitertippen. Das ist kein Zeitaufwand. Der entsteht immer dann, wenn ich zur Maus greifen muss, mich orientieren muss, wo sich mein Mauscursor befindet, dann damit hantieren muss und am Ende wieder die Maushand zur Tastatur zurückbewegen muss.Ralf hat geschrieben:Naja, das mit der Lesbarkeit ist so eine Sache. Wenn ich eine lange Methode habe (und die lassen sich teilweise nicht verhindern), muss ich ständig scrollen, weil diese Kommentare nicht im Blick sind. Das Problem hab ich bei Funktionsbausteinen oft. Da ist es mir doch lieber, ich kann die per F2 oder Mouseover nachschlagen.
Der Prozentsatz ist aber durchaus bedeutsam. Wenn man den Leuten allen Pistolen geben würde, dann würden sicherlich auch viele verantwortungsbewusst damit umgehen. Aber doch ein geringerer Prozentsatz als bei Messern. Also verbietet man die Pistolen.Ralf hat geschrieben:Das sehe ich komplett anders. Ich möchte mich nicht einschränken lassen von Leuten, die das Prinzip nicht verstanden haben.
Nur, weil sich tausende jeden Tag mit dem Messer schneiden, lasse ich mir meins doch nicht wegnehmen mit dem Hinweis, Messer seien gefährlich.
Damit stellst Du auf absolute Zahlen ab und hast damit recht. Das geht aber nicht in die Richtung, in der Du nach meinem Verständnis eigentlich argumentieren wolltest, nämlich dass prozedurale Programme eher schlampig geschrieben seien als objektorientierte. Bei dem Ansatz musst Du nämlich prozentual vergleichen und nicht in absoluten Zahlen, und dann glaube ich keineswegs, dass die objektorientierten Programme besser dastehen. Bei prozeduralen Programmen hat sich zumindest mal die Verhaltensweise weitgehend durchgesetzt, globale Variablen zu meiden. Dadurch, dass diese in OO zu "Attributen" geadelt worden sind, hat sich nach meiner Erfahrung die diesbezügliche Disziplin bei objektorientierten Programmen dramatisch verschlechtert. Was die Leute da machen, hat mit Zuständen, die das Objekt beschreiben sollen, meist nicht viel zu tun. Es wird einfach aus Bequemlichkeit mit klassenglobalen Feldern gearbeitet und fertig.Ralf hat geschrieben:Also, das kann ich fast gar nicht glauben, weil in JEDEM System in dem ich bisher gearbeitet habe, ist die Zahl prozeduraler Programm DEUTLICH höher als die Zahl objektorientierter Codingeinheiten.
Das würde man auch anders hinbekommen. Auch in einer einblöckigen Klasse (anstelle der beiden Blöcke "DEFINITION" und "IMPLEMENTATION") könnte man bei der Klassendefinition eine "INHERITING FROM"-Syntax verwenden. Und dann könnte man einfach sagen, jede Methode der Oberklasse, die in diesem einen Block nicht vorkommt, verwendet den Ursprungscode. Wenn einem das kosmetisch nicht gefällt (weil eine erbende Klasse dadurch Methoden hätte, die in der Klasse selbst nirgendwo sichtbar sind), dann könnte man auch eine Syntax METHOD geerbte_methode INHERITED definieren, die deutlich macht, dass die Methode als geerbter Code zur Verfügung steht. Da müsste man sich überlegen, was am Schönsten ist, aber das sind alles lösbare Probleme.black_adept hat geschrieben:Der Grund für die Trennung in Definition und Implementierung ist, dass es dadurch möglich ist das Coding in vererbten Methoden auszutauschen.
Letzteres finde ich auch nicht so schön. Und so "1-Pass" kann der ABAP Compiler gar nicht sein, denn er akzeptiert PERFORM-Aufrufe vor der Form, meckert aber, wenn es die Form gar nicht gibt. Ich verstehe nicht, weshalb das bei Klassen (oder, wie Du richtig sagst, auch Feldern) nicht möglich sein soll.black_adept hat geschrieben:Und was den Compiler und "Definition Deferred" angeht: Das Problem hat man eigentlich nur bei einem 1-Pass-Compiler und du hast das gleiche Problem auch an anderer Stelle weil du Variablen erst dann im Programm ansprechen kannst, nachdem (später im Code) sie definiert wurden.
Aus zwei Gründen: Erstens kannst Du dann in START-OF-SELECTION lokale Felder definieren, die nicht global und damit in den gerufenen Untermethoden nicht vorhanden sind (saubere Kapselung). Und zweitens brauchst Du dann nicht bei jedem Methodenaufruf den Namen der Klasse davorzuschreiben. Das sieht dann z.B. so aus:tar hat geschrieben:Wozu braucht man im Report eine zusätzliche Main-Methode?
Code: Alles auswählen.
START-OF-SELECTION.
lc=>main( ).
CLASS lc IMPLEMENTATION.
METHOD lc.
DATA(nutzdaten) = daten_einlesen( ).
daten_ausgeben( nutzdaten ).
ENDMETHOD.
METHOD daten_einlesen.
...
Und sowohl Dein eininstanziges Objekt user als auch die interne Tabelle user_used_passwords wären global. Sowas finde ich Mist. Vor allem, wenn dann die Methoden gerufen werden, die den von dir mit "... anzeigen, per Mail senden, Log schreiben, whatever ..." beschriebenen Teil implementieren, ist dann user_used_passwords sicherlich auch nicht Bestandteil der Parameterschnittstelle. Sowas finde ich hässlich und unsauber.tar hat geschrieben:Beispielsweise würde das (vereinfacht) so aussehen:
Tatsächlich sind die Methoden meiner obenstehenden LC-Klasse bis auf MAIN alle privat. Ausnahmen gibt es allenfalls dann, wenn durch Dynprotechnologie weitere Aufrufe von außerhalb der Klasse erfolgen müssen (beispielsweise bei einem Doppelklick ins erzeugte ALV, wo dann die Event-Handler-Klasse des ALVs meine lokale Klasse rufen muss).tar hat geschrieben:Eine irgendwie gerartete Main()-Methode einer rein statischen Klasse "LC" liefert dementgegen genau gar nichts an Informationsgehalt. Ganz schlecht wird es, wenn dort wie üblich einfach nur prozedural und durchweg mit PUBLIC Properties hantiert wird. Das ist einfach nur schlechtes Coding, faul programmiert, hingeschissen.
Ich kann mich an einen OO-ABAP-programmierenden Kollegen erinnern, dessen Methodenschnittstellen waren praktisch immer leer. In der Folge war sein Programm für Außenstehende praktisch nicht wartbar, und wir hatten die größten Probleme, als er das Unternehmen verlassen hat. Nun wirst Du sagen, dass das ein schlechter Mann gewesen ist, und da hast Du auch recht, aber das ist die Realität, wie ich sie erlebe. Viele Leute schreiben schlechten Code, ob es unsereins gefällt oder nicht. Und bei prozeduraler Programmierung hat man da noch eher eine Chance, das Programm reverse zu engineeren und irgendwie doch noch zu begreifen, wie es funktioniert.tar hat geschrieben:Das ist beides gruselig. Unverständliches kommt aber eher daher, dass man eine schlechte Codebasis über Jahre oder Jahrzehnte immer weiter ergänzt, statt zwischendurch mal grundlegend überarbeitet hat.
Das habe ich mir schon immer gedacht. Insofern Applaus für dieses Statement.Ralf hat geschrieben:Reine Setter- und Gettermethoden entsprechen eigentlich nicht im Stand der Technik. Denn hierzu muss man wissen, wofür die einzelnen (oft privaten) Attribute in der Klasse notwendig sind. Das sollte den Aufrufe aber eigentlich schon nicht interessieren.