CONSTRUCTOR & Co. [Sammeln & Seltenes]


Die Objektorientierung mit ABAP®: Vererbung, Dynamische Programmierung, GUI Controls (u.a. ALV im OO).

Moderatoren: Jan, Steff

CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon ewx » 02.01.2019, 20:21

Hallo zusammen,
in der letzten Zeit sind vermehrt Themen und Diskussionen aufgetaucht, die sich um die "richtige" Definition und Instantiierung von Objekten dreht.
  • Singleton
  • Multiton
  • Factory
  • get_instance
  • Constructor-Parameter (ja, nein, wie viele?)
  • usw

Es geht immer wieder darum, wann wie warum welche Sachen beachtet werden müssen.


Ich würde alles, was an diesem Thema hängt gerne mal irgendwie ordnen und zusammen fassen, da ich auch immer wieder unsicher bin, wann welche Methode die beste ist.

Mit einer Mindmap habe ich mal angefangen:

Bild

Da ist natürlich noch kein Coding dran und es ist auch nicht die ideale Darstellung für so ein komplexes Thema.
Ich würde aber trotzdem gerne eine Möglichkeit finden, wie man die einzelnen Aspekte einigermaßen kompakt darstellen kann.

Mann kann alles zu den einzelnen Themen nachlesen. Oft ist aber kein Beispiel vorhanden oder keines für ABAP oder ein völlig unrealistisches. Häufig wird auch nur beschrieben was etwas macht, aber nicht warum man es verwenden sollte oder wann nicht.

  • Was sind die Vorteile, wenn ein CONSTRUCTOR keine/wenige/viele Parameter hat?
  • Was sind die Nachteile?
  • Wie verhält es sich bei Erweiterungen, also wenn Parameter hinzukommen müssten?
  • Lässt sich das umgehen? Wenn ja, wie?
  • Worauf muss man aufpassen?
  • Wie sieht ein Beispiel aus der Praxis aus?
  • Wo gab es bei bisherigen Projekten Schwierigkeiten? Welche? Wie wurden sie gelöst?
  • Wann ist ein singleton *wirklich* sinnvoll?


Hierfür hätte ich gerne eure Ideen (Fachlich, inhaltlich, technisch, ...):
Wie kann man das alles so ordnen, dass man nicht zig Seiten Text lesen muss?
Welche Bereiche/ Spalten/ Themen sind sinnvoll?
Welches Tool wäre passend?


Natürlich sollen am Ende ein oder mehrere Artikel im Tricktresor dabei herauskommen, in dem man alles gesammelt nachlesen kann...! ;)

PS:
Code: Alles auswählen
DATA(year2019) = NEW year( 2019 ).
year2019->wish( 'Happy new year' ).
ewx
Top Expert
 
Beiträge: 3885
Registriert: 04.08.2003, 19:55
Wohnort: Schleswig-Holstein
Dank erhalten: 343 mal

Sponsor

Alte ABAP-Entwicklerweisheit: Weißt du weder aus noch ein, baust du einen BADI ein

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon deejey » 03.01.2019, 01:55

Ich habe relativ grobe OO-Kenntnisse, musste erst nachlesen was Singleton überhaupt bedeutet :D aber ich frage mich warum es wichtig ist wie viele Parameter der Constructor hat? Auch bei Erweiterungen sehe ich das Problem nicht, das Konzept optionaler Parameter ist doch perfekt und deckt vieles ab!? Und wenn es nicht optional ist, dann müssen eben alle Aufrufe angepasst werden, denn die fachliche Anforderung des neuen Parameters hatte ja einen Grund, den man meist nicht vorab ahnen konnte oder wollte.
deejey
Specialist
 
Beiträge: 182
Registriert: 31.07.2016, 11:20
Dank erhalten: 14 mal
Ich bin: Entwickler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon SaskuAc » 03.01.2019, 08:30

Also grundsätzlich glaube ich kann man festhalten, dass es keine allgemein gültige Lösung, bzw. "die Richtige Art und Weise" gibt, wie man Objekte instantiiert.

Persönlich bin ich ein Freund davon, eine "get_instance" Methode zu haben, welche dann das Objekt zurück liefert. Man kann hier nämlich im Hintergrund Sachen erledigen bzw. Logiken austauschen, welche bei einem Constructor nicht geht. ( z. B. kann man, wenn es Später dann mal Nötig ist, die Klasse zu einem Singleton ummodeln, oder aber zu einem Multiton ( was ich in deiner Mindmap zum ersten Mal höre, aber klingt mir einleuchtend, wofür man das brauchen kann )

Danach setzt man mit verschiedenen Public Setter-Methoden die jeweiligen Attribute. Ist auch für die Wartbarkeit und Testbarkeit ( speziell für Unit Tests ) extrem gut.

Wenn man dennoch einen Constructor verwendet, dann bitte wenige Parameter, da hier ( wie überall wo extrem viele Parameter auftauchen ) die Wartbarkeit leidet. Bei wenigen Parametern hat man dementsprechend den Vorteil, dass nicht so viele Programme angefasst werden müssen, wenn ein neues Attribut, welches man evtl. im Constructor initialisiert, hinzukommt. - dafür auch wieder eine Setter-Methode und fertig. Nicht jedes Programm wird dieses neue Attribut brauchen - man müsste es aber anfassen, wenn man den Constructor ändert ( wobei man ja den parameter auf optional setzen kann - bin aber absolut kein freund davon! ). Man soll einfach danach die Setter Methode aufrufen und fertig.

Edit:
deejey hat geschrieben:Ich habe relativ grobe OO-Kenntnisse, musste erst nachlesen was Singleton überhaupt bedeutet :D aber ich frage mich warum es wichtig ist wie viele Parameter der Constructor hat? Auch bei Erweiterungen sehe ich das Problem nicht, das Konzept optionaler Parameter ist doch perfekt und deckt vieles ab!? Und wenn es nicht optional ist, dann müssen eben alle Aufrufe angepasst werden, denn die fachliche Anforderung des neuen Parameters hatte ja einen Grund, den man meist nicht vorab ahnen konnte oder wollte.


Naja, es leidet dennoch die Testbarkeit und lesbarkeit. ich finde:
Code: Alles auswählen
new Year( year = '2019'
                         wish = 'Happy new Year'
                         people_on_earth = 8.000.000.000 " nicht wirklich
                         etc = etc
                         usw = usw ).
 

weniger lesbar als
Code: Alles auswählen
data(year) = Year=>get_instance( year = '2019' ).
year->set_wish( 'Happy new Year' ).
year->set_people( 8.000.000.000 ).
year->set_etc( etc ).
year->set_usw( usw ).
 


natürlich besteht hier mehr tipparbeit und es ist natürlich auch eine sehr Subjektive Sache. - Dennoch finde ich das schöner. Abgesehen davon ist das sehr viel einfacher zu testen und, für den Fall dass Fehler auftreten leichter zu behandeln. Abgesehen davon ist OO dafür da, dass stark gekapselt wird ( ich werde unten das protected prinzip ausklammern ) - der Konstruktor kapselt aber überhaupt nicht. Mal von der Kapselung abgesehen ist ein anderes OO-Prinzip verletzt, wenn man viele Parameter rein haut: Man soll kleine Methoden schreiben. Da du im Konstruktor alle Parameter auf Gültigkeit prüfen musst und danach dann noch verarbeiten ( z. B. zum Attribut setzen - was ja noch das geringste wäre ) musst, können hier schnell riesige Construktor-Methoden entstehen, was nicht schön ist ( und im Construktor dann die setter methoden aufrufen ergibt für mich keinen Sinn .. da kann man die auch gleich von außen aufrufen ... da erspart man sich dann wieder tipparbeit ).
Die Kritik hier wäre nicht relevant, wenn es in ABAP das Konstrukt mit "Überladenen"-Methoden gäbe. Heißt 2 Methoden mit dem selben Namen, aber mit unterschiedlicher Signatur ( heißt in dem Fall, Importparametern ). Dann könnte man einfach den Constructor wählen den man haben will, mit Parameter oder ohne und dann bei bedarf die Setter-Methoden rufen.

Endedit.

Bezüglich der Sichtbarkeit - es kommt ein wenig drauf an wie man nun das Objekt instantiieren möchte. Wie oben schon gesagt, gibt es keine allgemein gültige Lösung. Protected habe ich in ABAP ( und auch anderen Sprachen um ehrlich zu sein ) noch nie verwenden können. ( Python unterstützt soweit ich weiß noch nicht einmal diese Art der Kapselung hin zu private ) - Und um ehrlich zu sein, habe ich noch kein einziges ABAP Programm gesehen, welches Protected irgendwie verwendet .. . Klar, wenn man ganz streng nach OO-Konzept geht, sollte man es verwenden, aber es ist mM nicht relevant. Zu Public schreibst du
"Aufrufer muss über Parameter / Attribute bescheid wissen"
- das muss er aber auch bei einem private. Wenn man irgendwie "von außen" ein Attribut verändern möchte, muss man wissen wie das Beschaffen ist, sonst funktioniert das keinesfalls. Oder übersehe ich hier etwas?

Zum Singleton ist es relativ einfach Beispiele zu finden. Eines der besten ist, wie du schon schreibst eine Log-Klasse. Oder ein anderes Beispiel aus unserer Firma. Wir haben einen "Verbucher-Report" - welcher, je nach angegebener Zeit, Sachen verarbeitet. Die Klasse die dafür zuständig ist, die Daten zu verarbeiten, ist ein Singleton. Das brauchen wir, weil sich die "Updatetasks" sonst in die quere kommen könnten und dann eventuell Tabellen ( bei uns im HR-Umfeld eher Personen ) sperren oder auch teilweise Laufzeitfehler verursachen könnten.

Bei der Verwendung von Festwerten wenn möglich, falls man auf einem 7.51 System ( oder höher ) sitzt, dann Enumeratoren ( kurz enums ( sing. Enum ) ) verwenden. Wenn man einen Parameter hat, welcher auf einem Enum aufbaut, prüft der Syntaxcheck gleich ob das verwendet worden ist. Dadurch spart man sich die Prüfung ob der mitgegebene Parameter gültig ist oder nicht.

Zum Punkt "Wann ist welcher Anwendungsfall am besten":
Wenn es Klassen gibt - bei der man nur eine Methode nach Instantiierung aufruft finde ich das Konstrukt am besten:
Code: Alles auswählen
" gut lesbarer einzeiler, oder?
new Year( '2019' )->wish( 'Happy new Year' ).
 

Danach verschwindet das Objekt nämlich wieder aus dem Speicher und alles ist gut. Das "new"-Konstrukt ( also der normale Konstruktor aufruf ) nehme ich hauptsächlich dann her, wenn ich ein Objekt als Parameter irgendwo übergeben muss:

Code: Alles auswählen
data(greetings) = newYearGreetings=>get_instance( new Year( Year=>enum-2019 ) ).
greetings->wish( ). " output is 'Happy new Year'
 


Wenn man mit dem Objekt weiterarbeiten möchte ( wie z. B. dem Coding darüber )
Code: Alles auswählen
" entweder - ist leichter zu lesen, wenige Parameter vorausgesetzt
data(year2019) = Year=>get_instance( Year=>enum-2019 ).

" oder -  finde ich aus wartungs- und testsicht besser
data(2_year2019) = Year=>get_instance( ).
2_year2019->set_year( Year=>enum-2019 ).

year2019 = 2_year2019.

year2019->wish( 'Happy new Year' ).
year2019->vergesse_vorsätze( abap_true ).
" ...
 


Zum Punkt, wie man das gut Visualisieren und Aufbereiten kann:
finde deine MindMap von der Struktur her nicht schlecht. - Es muss halt nur richtig zugeordnet sein. Wie oben schon beschrieben, finde ich den Punkt bei Public jetzt nicht so Richtig..
Wie man das aufbereiten kann ohne ewig viel Text, nur durch Beispiele, glaub ich. Tatsächlich habe ich gerade das Buch "Entwurfsmuster in ABAP" neben mir liegen. Die Texte die drinnen stehen, sind nicht wirklich erklärend, die Beispiele aber schon. wahrscheinlich wäre es gut, wenn du das auch mit einigen kleinen Code-Abschnitten auf Tricktresor dann darstellen könntest.

Naja, das wars jetzt dann erstmal mit einem kleinen Monolog. :D Hoffe das war nicht allzu durcheinander :?
Zuletzt geändert von SaskuAc am 03.01.2019, 08:57, insgesamt 2-mal geändert.

Für diese Nachricht hat SaskuAc einen Dank bekommen :
ewx
SaskuAc
Specialist
 
Beiträge: 234
Registriert: 01.06.2015, 10:16
Dank erhalten: 23 mal
Ich bin: Entwickler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon nickname8 » 03.01.2019, 08:36

deejey hat geschrieben:...das Konzept optionaler Parameter ist doch perfekt und deckt vieles ab!?


Ich halte das Konzept der optionalen Parameter für schlecht in der Form wie es jetzt besteht. Hier wird oft versucht mehrere ähnliche use-cases in einer Methode zu erschlagen und das wird dann über eine jeweilige Kombination von optionalen Parametern erreicht. Wenn es in ABAP überschreibbare Methoden gäbe, würden die optionalen Parameter auch wirklich Sinn machen. Da ist optional dann auch echt optional und nicht ein hässliches "entweder so oder so, aber eins von den beiden Kombinationen musst du schon bedienen, sonst dumpts".

Ansonstens: Super Thread. Bin sehr gespannt was man hier mitnehmen kann.

@ewx: du fragtest nach einen echten Usecase für einen Singleton. Wir nutzen für unsere Handscanner im WHS ein Fremwork welches Funktionsgruppenbasiert realisiert wurde vom Hersteller. Bedeutet: Jeder Screen (ver)braucht zwei FUBAs pro Screen (PBO und PAI). Ein Screen kann man sich vorstellen als Dynpro (ganz grob, nur um eine Idee zu bekommen). Bedeutet, man kann pro Funktionsgruppe 49 Screens erstellen (max. FUBAs pro FUGRU = 99 / 2 = 49,5 --> 49 Screens). Heißt, ich kann Daten/Objekte zwischen Screens aus verschiedenen Funktionsgruppen nicht global in einem Include des Rahmenprogramms speichern, da die andere Gruppe dieses Include nicht kennt (bzw wenn ich es auch in der anderen Gruppe einbinde ist es für dieses Rahmenprogramm nicht die selber Instanz, ich hoffe das war verständlich). Hier brauchte ich das selbe Objekt in allen Screens und das habe ich über ein Singleton gelöst.

Für diese Nachricht hat nickname8 einen Dank bekommen :
ewx
nickname8
ForumUser
 
Beiträge: 99
Registriert: 18.07.2015, 08:22
Dank erhalten: 12 mal
Ich bin: Entwickler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon ralf.wenzel » 03.01.2019, 11:25

Interesaantes Thema, gerade auch für mich, der immer wieder „schlechtes OO“ überarbeiten muss, um es objektorientierter zu machen. Schlechtes OO ist schlimmer als gar kein OO..... Und Objektorientierung fängt eben mit der Objekterzeugung an.

ewx hat geschrieben:Hallo zusammen,
in der letzten Zeit sind vermehrt Themen und Diskussionen aufgetaucht, die sich um die "richtige" Definition und Instantiierung von Objekten dreht.
  • Singleton
  • Multiton
  • Factory
  • get_instance
  • Constructor-Parameter (ja, nein, wie viele?)
  • usw


Das hängt immer vom Einzelfall ab. Ein Singleton nutzt man, wenn man nur ein Objekt braucht. Ich brauche keine n Instanzen einer Persistenzklasse, um n MARA-Sätze zu speichern, sondern nur eine. Ich brauche auch nicht n Instanzen einer UI-Klasse für einen Dialog. Ein Singleton schützt mich einfach davor, versehentlich den Speicher mit Instanzen zuzunageln.

Ein Multiton (SaskuAc liest meine Postings scheinbar nicht, ich hab schon vor Jahren das Pattern beschrieben) brauche ich, um mehrere Instanzen zu verwalten, weil ich z. B. eine Reihe von Materialstämmen verwalten will und verhindern will, dass ich für einen Materialstamm mehrere Objekte habe, die ggf. widersprüchliche Sachen tun. Im Grunde ist es es wie ein Singleton mit einer Tabelle als Attribut. Beides nutzt man, um es nicht dem Verwender zu überlassen, wie und wann ein Objekt erzeugt wird.

Eine Factory verwendet man, um die (Verantwortung für die) Instanziierung nicht nur vom Verwender abzukoppeln, sondern auch vom Erzeuger. Weil zum Beispiel das Objekt einer Subklasse erzeugt wird und der Erzeuger gar nicht weiß, welche Subklasse das Objekt erzeugt. So wird dann die wirklich objekterzeugende Klasse austauschbar, änderbar, erweiterbar. Das ist insbesondere bei der Frage der Testbarkeit von Belang, wie austuschbar die Implementierungen sind.

Der Constructor wird ohnehin immer durchlaufen, aber es hat sich gezeigt, dass es selten sinnvoll ist, ihn auszuprägen, um Vorgabewerte zu vergeben. Grund: Ändert sich die Logik oder der Typ für einen Vorgabewert, muss der constructor geändert und bezüglich seiner kompletten Funktionalität getestet werden. Verwende ich setter-Methoden, ändere ich nur das Coding für diesen einen Vorgabewert, was den Testaufwand senkt. Dann muss natürlich klar sein, welche Setter-Methoden unbedingt durchlaufen werden müssen, damit das Objekt konsistent ist. Am Besten realisiert man das in einer Methode, die eben diese Konsistenz prüft, ehe das Objekt zu arbeiten beginnt und durch sprechende Unit-Tests, die dem Entwickler zeigen, wie er mit der Klasse umzugehen hat. Doku wäre natürlich auch nicht schlecht, aber ich bin ja bescheiden geworden.

Für den Constructor gilt, was für alle Methoden gilt: Viele Parameter deuten darauf hin, dass in der Methode prozedural programmiert wurde. Komplexe Daten übergibt man am Besten als komplexe Struktur oder gleich als Objekt, weil sich die Schnittstelle nicht (oder automatisch mit-)ändert, wenn sich die übergebenen Parameter ändern. Es muss sich für einen Parameter nur die Konsistenzprüfung ändern, schon muss muss man ganzen Konstruktor neu testen. Außerdem führen viele Parameter bei jeder Methode zu vielen Testläufen, weil (wenn man es ordentlich macht) jedes Testtupel von mindestens einem Testlauf getroffen wird.

ewx hat geschrieben:
  • Was sind die Vorteile, wenn ein CONSTRUCTOR keine/wenige/viele Parameter hat?
  • Was sind die Nachteile?
  • Wie verhält es sich bei Erweiterungen, also wenn Parameter hinzukommen müssten?
  • Lässt sich das umgehen? Wenn ja, wie?
  • Worauf muss man aufpassen?
  • Wie sieht ein Beispiel aus der Praxis aus?
  • Wo gab es bei bisherigen Projekten Schwierigkeiten? Welche? Wie wurden sie gelöst?
  • Wann ist ein singleton *wirklich* sinnvoll?


Wenige Parameter sind immer gut, keine sind noch besser, das gilt nicht nur für Konstruktoren, schon der vielen Testtupel wegen. Wenn man viele Parameter hat, sind sie in der Regel (nach meiner Erfahrung) schlecht oder gar nicht zusammengefasst. Wenn mehrere Variablen einen semantischen Zusammenhang haben, gehören sie in eine Struktur oder ein Objekt.

Wenn Parameter hinzukommen, behilft man sich zumeist mit optionalen Parametern. Das ist schnell und billig, aber nicht gründlich, weil man darüber immer wieder stolpern wird. Die Alternative eines Refactorings (eines Überarbeitens aller alten Aufrufstellen) sollte zumindest erwogen (und aufwandstechnisch durchgerechnet) werden. Der Grundfehler ist aber oben schon beschrieben: Übergebe ich Daten in einer komplexen Struktur oder einem Objekt, kann mir das fast nicht passieren, weil ich den Typen jederzeit anpassen kann, ohne die Schnittstelle ändern zu müssen. Und *eigentlich* ist Objektorientierte Programmierung die „Programmierung der *Kommunikation* von Objekten“ (weshalb die Erfinder des OO den Namen bis heute falsch finden, weil es eigentlich eine kommunikationsorientierte Programmierung ist). Nach meinen Erfahrungen ist DAS der Punkt, den viele im OO arbeitenden Entwickler gar nicht verstanden haben: Es geht nicht um die Objekte an sich, sondern um die Kommunikation zwischen ihnen. Darum arbeitet man mit Interfaces (die ja eben diese Kommunikation klassenübergreifend festlegen), und darum sind gute Methodenschnittstelle so wichtig. Im Grunde kann man sagen: An der Zahl der Methodenaufrufe kann man ungefähr ermessen, wie gut OO verstanden wurde. Ein mächtiger Konstruktor (eine Methode) mit vielen Parametern im Vergleich zu einem kleinen Konstruktor (mit weniger Parametern) mit vielen settern ist ein Hinweis auf wenig durchdachtes OO. Natürlich kann man sich hinstellen und sagen „Was soll eine Methode mit einer Anweisung?“, aber es gibt eben triftige Gründe dafür. Sogar eine implementierte Methode OHNE Anweisung kann sehr wichtig sein.

Und ehe jetzt wieder wer meine Worte auf die Goldwaage legt: Nein, es ist nicht alleiniges oder primäres Kriterium, aber ein Hinweis darauf. Natürlich kann man auch mit vielen Methodenaufrufen schlechtes OO schreiben. Es ist aber schwierig, mit wenigen Methoden gutes OO zu schreiben, weil die Methodenaufrufe ja die Kommunikation zwischen den Objekten darstellen.

Ich hatte neulich den Fall, dass jemand Datum und Uhrzeit an eine Methode übergeben hat, damit sie sich einen Zeitstempel errechnen kann. Ersetzt habe ich die zwei Parameter durch ein Interface, das von einer Klasse implementiert ist, die einen Zeitstempel ermittelt. So habe ich nur einen Parameter und wenn ich zum Zeitstempel noch etwas anderes brauche (meinerwegen einen zweiten), ändere ich die Schnittstellendefinition, einen optionalen Parameter brauche ich dazu nicht. Bei einem Parameter ohne Verhalten reicht dann auch eine tiefe Struktur. Aber oftmals kommt dann eben doch Verhalten dazu....

Zum Singleton habe ich ja oben schon einiges geschrieben. In der Regel ist es so, dass Akteure (Belege, Lieferungen, etc.) kein Singleton haben sollten, technische Klassen aber eben doch. Beispiele sind Persistenzklassen, die ja auch nur EINE Tabelle repräsentieren, Factory-Klassen, etc.

Interessanterweise erhalte ich Rückendeckung für meine Entwicklungsmethode (private Instanzenbildung mit Erzeugermethode), die hier kürzlich kritisiert wurde, vom Autor des Buches „Agile ABAP Entwicklung“, der deutlich für diese Technik plädiert.

ewx hat geschrieben:Natürlich sollen am Ende ein oder mehrere Artikel im Tricktresor dabei herauskommen, in dem man alles gesammelt nachlesen kann...! ;)


Wo DU dann schreibst, was für tolle Ideen du hattest? LOL


Ralf
Zuletzt geändert von ralf.wenzel am 03.01.2019, 16:25, insgesamt 1-mal geändert.

Für diese Nachricht hat ralf.wenzel 2 Dankeschön bekommen :
ewx, Tommy Nightmare
ralf.wenzel
Top Expert
 
Beiträge: 3374
Registriert: 18.09.2004, 13:03
Wohnort: Hamburg
Dank erhalten: 213 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon ewx » 03.01.2019, 13:57

Ralf.Wenzel hat geschrieben:Wo DU dann schreibst, was für tolle Ideen du hattest? LOL

Ganz genau! 8)
ewx
Top Expert
 
Beiträge: 3885
Registriert: 04.08.2003, 19:55
Wohnort: Schleswig-Holstein
Dank erhalten: 343 mal

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon a-dead-trousers » 03.01.2019, 21:03

Ich bin ne faule Sau und deher hatte ich nicht die Zeit alle Postings zu lesen, aber ein Prolem über das ich immer wieder stolpere ist, dass man CONSTRUCTOR-Methoden und daraus aufgerufene Methoden (in ABAP) NICHT so überladen kann wie erwartet. Deswegen baue ich in meinen ganzen Frameworks z,B. eine INITIALIZE Methode ein die grundsätzlich den Constructor ablöst und wie aus anderen Programmiersprachen erwartet überladen werden kann.
Das Problem ist, dass ABAP im Constructor (und nur da) die Vererbung ignoriert. Das bedeutet, dass bei einer Methode die im Constructor einer untergeordneten Klasse aufgerufen wird, nicht die überladene Methode einer übergeordneten Klasse aufgerufen wird sondern nur die aktuelle Methode der untergeordneten Klasse.
Grundsätzlich kein Drama, wenn man weiß, dass sich ABAP so verhällt, aber dennoch ein Problem für jemanden, der aus einer anderen Programmierumgebung kommt. Man kann dies mit Framworks umgehen die z.B. eine INITIALZE-Methode verwenden die zwingend nach dem CONSTRUCTOR aufgerufen werden muss und somit die spezielle ABAP-Vorgabe der Constructor-Aufruf-Hierarchie umgeht.

Wenn das jemand anderes hier bereits eingebracht hat, möchte ich mich enschuldien, aber ich wollte diesen Punkt weder unter den Tisch fallen gelassen noch zu sehr hervorgehoben wissen.

lg ADT
Theory is when you know something, but it doesn't work.
Practice is when something works, but you don't know why.
Programmers combine theory and practice: Nothing works and they don't know why.

ECC: 6.07
Basis: 7.40

Für diese Nachricht hat a-dead-trousers einen Dank bekommen :
ewx
a-dead-trousers
Top Expert
 
Beiträge: 3179
Registriert: 07.02.2011, 13:40
Dank erhalten: 789 mal
Ich bin: Entwickler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon ralf.wenzel » 03.01.2019, 21:13

Nein, das hatte noch keiner erwähnt, auch ich hätte es im Kopf, aber dann vergessen. Das ist aber ein wichtiger Hinweis, weil viele das nicht wissen und es ein wahrlich überraschendes Verhalten darstellt. Und du hast recht: Es ist ein Argument, möglichst wenig direkt in den Konstruktor zu schreiben.

Du solltest aber ein wenig auf deine Wortwahl achten: Du meinst z. B. etwas anderes als überladen (das Überladen von Methoden wird in ABAP grundsätzlich nicht unterstützt). So kommt es leicht zu Missverständnissen.



Ralf

Für diese Nachricht hat ralf.wenzel einen Dank bekommen :
a-dead-trousers
ralf.wenzel
Top Expert
 
Beiträge: 3374
Registriert: 18.09.2004, 13:03
Wohnort: Hamburg
Dank erhalten: 213 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon a-dead-trousers » 03.01.2019, 22:43

ralf.wenzel hat geschrieben:Du solltest aber ein wenig auf deine Wortwahl achten: Du meinst z. B. etwas anderes als überladen (das Überladen von Methoden wird in ABAP grundsätzlich nicht unterstützt). So kommt es leicht zu Missverständnissen.

Sorry, wenn man eine Methode (theotetisch) durch eine ander ersetzen kann, war für mich das immer "überladen", aber du meinst das "überladen" einer Methode auch mit anderen Parametern, was in ABAP natürlich leider von jeher nie funktioniert hat. Peinlich jetzt, dass ich mich an die genaue Bezeichnung für diese Art der Methoden-Ersetzung nicht mehr erinnern kann. :oops:
(und ich komm ursprünglich aus der Java-Welt)
Man merkt dass ich mich in den letzten Jahr(zehnt)en zu sehr auf ABAP konzentriert habe.

EDIT:
Ein guter Freund hat mich gerade in Java bzw C++ darüber aufgeklärt, dass es sowohl die OWERWRITE als auch die OVERLOAD Anotation gibt. Damit wird dort die Unterscheidung zwischen diesen beiden Fälle deklariert.
Theory is when you know something, but it doesn't work.
Practice is when something works, but you don't know why.
Programmers combine theory and practice: Nothing works and they don't know why.

ECC: 6.07
Basis: 7.40
a-dead-trousers
Top Expert
 
Beiträge: 3179
Registriert: 07.02.2011, 13:40
Dank erhalten: 789 mal
Ich bin: Entwickler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon ralf.wenzel » 04.01.2019, 07:41

a-dead-trousers hat geschrieben:
ralf.wenzel hat geschrieben:Du solltest aber ein wenig auf deine Wortwahl achten: Du meinst z. B. etwas anderes als überladen (das Überladen von Methoden wird in ABAP grundsätzlich nicht unterstützt). So kommt es leicht zu Missverständnissen.

Sorry, wenn man eine Methode (theotetisch) durch eine ander ersetzen kann, war für mich das immer "überladen",


Du suchst nach dem Wort „Redefinieren“.

Hinzufügen wollte ich aber mich, dass die Ansätze von Enno sich keinesfalls gegenseitig ausschließen. Am Beispiel einer Factory: Die ist stets selbst ein Singleton und kann auch Singletons oder Multitons erzeugen.

Hinweis an Enno: Unterscheide zwischen abstrakter und konkreter Factory, das sind grundsätzlich verschiedene Dinge.


Ralf
ralf.wenzel
Top Expert
 
Beiträge: 3374
Registriert: 18.09.2004, 13:03
Wohnort: Hamburg
Dank erhalten: 213 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon black_adept » 04.01.2019, 08:03

  • Kann man eigentlich sagen, dass die Methode "GET_INSTANCE" eine Factory-Methode für den Singleton-Pattern ist?
  • Sollte man bei Verwendung von Factories die Instanzerzeugung der Klasse auf "Private" stellen
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de
black_adept
Top Expert
 
Beiträge: 3195
Registriert: 08.01.2003, 13:33
Wohnort: Lehrte ( bei Hannover )
Dank erhalten: 559 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon black_adept » 04.01.2019, 08:58

ralf.wenzel hat geschrieben:Ich hatte neulich den Fall, dass jemand Datum und Uhrzeit an eine Methode übergeben hat, damit sie sich einen Zeitstempel errechnen kann. Ersetzt habe ich die zwei Parameter durch ein Interface, das von einer Klasse implementiert ist, die einen Zeitstempel ermittelt. So habe ich nur einen Parameter und wenn ich zum Zeitstempel noch etwas anderes brauche (meinerwegen einen zweiten), ändere ich die Schnittstellendefinition, einen optionalen Parameter brauche ich dazu nicht. Bei einem Parameter ohne Verhalten reicht dann auch eine tiefe Struktur. Aber oftmals kommt dann eben doch Verhalten dazu....
Das ist ein Paradebeispiel dafür, dass ich mich manchmal bei Ralfs Ausführungen frage: "Warum" oder "Was zum Geier meint er hier"?
Wenn ich das richtig verstanden habe ist jetzt folgendes entstanden:
Vorher:
Code: Alles auswählen
lv_timestamp = get_timestamp( iv_datum = ...  iv_uzeit = ... ).
...
method get_timestamp.
  berechne irgendwie den Zeitstempel
endmethod.

Nachher:
Code: Alles auswählen
lo_implementation = get_instance_timestamphelperclass( ... ).
lo_implementation->set_time( ... ).
lo_implementation->set_data( ... ).
Entweder das hier
  lv_timestamp = get_timestamp( lo_implementation ).
oder das hier
  lv_timestamp = lo_implementation->get_timestamp( ).

method get_timestamp.
  berechne irgendwie den Zeitstempel
endmethod.
method get_implementation.
...
endmethod.
method set_time.
...
endmethod.
method set_data.
...
endmethod.

 
Aber das kann ich mir kaum vorstellen. Lieber Ralf - helle mich mal auf was für ein Konstrukt du jetzt verwendet hast.
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de
black_adept
Top Expert
 
Beiträge: 3195
Registriert: 08.01.2003, 13:33
Wohnort: Lehrte ( bei Hannover )
Dank erhalten: 559 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon ralf.wenzel » 04.01.2019, 10:40

So ungefähr stimmt das, was du schreibst. Es sieht natürlich deutlich mehr aus, weil du „bei mir“ zwei Varianten zeigst. In Wahrheit verwende ich kaum mehr Coding als du. Der Punkt aber ist:

• Ich fasse die Daten zusammen, die zusammengehören
• Bei mir ändert sich die Methodenschnittstelle nicht, wenn die Definition des Timestamps sich ändert
• Ich habe einen Verwendungsnachweis auf die Timestamp-Verwendungen

Gerade beim ersten Punkt vermisse ich oft das Sehen der Datensemantik. Im Kontext des Timestamps sind Datum und Uhrzeit nur ein Teil der Daten. So wie ich nie auf die Idee kommen würde, in zwei verschiedenen Parametern erst die ersten 10 Stellen und dann den Rest einer Materialnummer zu übergeben, würde ich Datum und Zeit nie in Einzelfeldern übergeben in einem Kontext, wo sie nur gemeinsam zu betrachten sind. Also gehören Datum und Uhrzeit zumindest in eine Struktur. Weil zusammengehört, was zusammen betrachtet wird.

Nun haben wir hier nicht nur nicht-elementare Daten zu verwenden, sondern ein für diese Daten typisches Verhalten zu implementieren. Und beim Wort „datentypisches Verhalten“ (oder „Daten mit datenspezifischer Logik“) bin ich eben beim Objekt.

Zu deiner Frage „Was will der eigentlich?“ sage ich nur „Dokumentation“. Das Interface sollte dokumentiert und mit einem sprechenden Namen versehen sein. Dann fragt man sich das gar nicht erst. Schwerer zu lesen finde ich das nicht, zumal der eigentliche Aufruf sich ja sogar vereinfacht (nur ein Parameter statt zweien).

Der Grundirrtum ist eben oft „Die Instanz einer Klasse ist ein Objekt“. Ich gehe andersherum da ran (nämlich so, wie Alan Kay es definiert hat und er hat es immerhin erfunden): „Alles ist ein Objekt“. Ich mache also kein Objekt, weil ich eine Klasse habe, sondern ich schreibe eine Klasse, weil ich ein Objekt brauche. Das ist z. B. der Fall, wenn ich ein komplexes Datenobjekt habe, das über spezifisches Verhalten verfügen muss.

Ich habe selbst interne Tabellen manchmal in einer Klasse verschalt (mit einem Iterator-Pattern), wenn ich spezielle Eigenschaften brauchte, die man programmieren muss oder sogar Strukturen. Beispiel: Beim Aufbau von Materialstammsätzen für MATERIAL_MAINTAIN_DARK braucht man eine spezielle Datenhierarchie: Wenn ich zwei MARD-Sätze habe, brauche ich für jeden MARD-Satz auch einen korrespondierenden MARC- und und zu jedem MARC-Satz einen korrespondierenden MARA-Satz, so dass ein (!) Materialstamm durchaus aus 6 MARA-Sätzen bestehen kann.

Bei mir prüft ein MAR*-Satz ob ein übergeordneter Satz vorhanden ist. Ist dem nicht so, tritt er die Erzeugung an. Das ist viel einfacher als manueller Abgleich und muss nur einmal implementiert werden, um für beliebig viele Materialstämme zu funktionieren. So erhalte ich quasi „intelligente“ Strukturen, die selbst für ihre Konsistenz sorgen.


Ralf
ralf.wenzel
Top Expert
 
Beiträge: 3374
Registriert: 18.09.2004, 13:03
Wohnort: Hamburg
Dank erhalten: 213 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon black_adept » 04.01.2019, 10:57

ralf.wenzel hat geschrieben:So ungefähr stimmt das, was du schreibst. Es sieht natürlich deutlich mehr aus, weil du „bei mir“ zwei Varianten zeigst. In Wahrheit verwende ich kaum mehr Coding als du.

Es sieht nicht nur so aus - es ist deutlich! mehr Coding. Letztlich habe ich das Gefühl, als ob du eine (bisher wohl nur lokal) verwendete Methode in eine Helperklasse verschiebst. Macht ja bei mehrfach verwendbaren Methoden durchaus Sinn - aber das war irgenwie nicht der Tenor deiner ursprünglichen Ausführung.
ralf.wenzel hat geschrieben:• Ich fasse die Daten zusammen, die zusammengehören
Das machte die ursprüngliche Methode auch, indem sie hoffentlich 2 Pflichtparameter verlangt hat. Gibt halt verschiedene Wege nach Rom
ralf.wenzel hat geschrieben:• Bei mir ändert sich die Methodenschnittstelle nicht, wenn die Definition des Timestamps sich ändert
WAS?
ralf.wenzel hat geschrieben:• Ich habe einen Verwendungsnachweis auf die Timestamp-Verwendungen
Die hatte man vorher auch durch den Verwendungsnachweis der Methode.
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de
black_adept
Top Expert
 
Beiträge: 3195
Registriert: 08.01.2003, 13:33
Wohnort: Lehrte ( bei Hannover )
Dank erhalten: 559 mal
Ich bin: Freiberufler/in

Re: CONSTRUCTOR & Co. [Sammeln & Seltenes]

Beitragvon erp-bt » 04.01.2019, 11:04

black_adept hat geschrieben:
  • Kann man eigentlich sagen, dass die Methode "GET_INSTANCE" eine Factory-Methode für den Singleton-Pattern ist?


Nein, könnten zur Laufzeit ja ganz unterschiedliche Instanzen geholt werden. Je nachdem was man gerade benötigt. GET_INSTANCE ist einfach das delegieren der Instanzerzeugung, kann dann natürlich auch ein Singleton sein.

black_adept hat geschrieben:
  • Sollte man bei Verwendung von Factories die Instanzerzeugung der Klasse auf "Private" stellen


Sollte so sein. Wenn Du eine separate Factory-Klasse hast, muss diese allerdings dann als "Friend" der eigentlich zu instantiierenden Klasse definiert werden.

Liebe Grüße, Tapio
...entwickelnder Berater...beratender Entwickler
erp-bt
Specialist
 
Beiträge: 154
Registriert: 14.05.2008, 10:49
Dank erhalten: 19 mal

Nächste

Zurück zu ABAP Objects®

  Aktuelle Beiträge   
Java Definition Interface/Abstrakte Klasse
vor 2 Stunden von nickname8 1 Antw.
WF - Containeroperation
vor 7 Stunden von bapimueller 0 Antw.
gelöst BANF-Freigabe
vor 7 Stunden von schusterd 1 Antw.
gelöst Serialnummern bei BANFen/Bestellungen angeben
vor 7 Stunden von mareikemei92 3 Antw.
Datumsdifferenz berechnen
Gestern von DeathAndPain 3 Antw.

  Ähnliche Beiträge beta
Haste mal nen Constructor
01.12.2005, 16:21 von ereglam 1 Antw.

 

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder