AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Delphi Konzeptfrage: Interface oder Messages
Thema durchsuchen
Ansicht
Themen-Optionen

Konzeptfrage: Interface oder Messages

Ein Thema von Ghostwalker · begonnen am 9. Nov 2016 · letzter Beitrag vom 10. Nov 2016
Antwort Antwort
Seite 2 von 3     12 3      
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#11

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 16:42
und noch ein wenig Code zum Beispiel, in welche Richtung hier meine Gedanken gehen:

Dat Interface:

Delphi-Quellcode:
TYPE
   TMasterData = record
                  id : integer;
                  status : integer;
                  text : String;
   end;

   IDetail = INTERFACE;

   IMaster = Interface
     PUBLIC
       function getItem(index:integer):TMasterData;
       procedure setItem(const index:integer;const data:TMasterdata);
       Property Items[index:integer]:TMasterData read getItem write setItem;

       procedure setDetail(element:IDetail);
       Property Detail:IDetail write setDetail;

       Procedure NewElement(data:TMasterdata);
       Procedure DeleteElement(data:TMasterdata);
   End;

   IDetail = Interface
     PUBLIC

       Procedure SetMaster(element:IMaster);

       Property Master:IMaster write setMaster;

       Procedure AddNew();
       Procedure Edit(id:integer);
       procedure Delete(id:integer);
   End;
Da Master
Delphi-Quellcode:
uses
   MyInterfaces;

TYPE
  PMasterdata = ^TMasterdata;

  TNavi = class(TObject,IMaster)
    PRIVATE
       datalist : TList;
    PROTECTED
       :
       :
       :
    PUBLISHED
       :
       :
       :
    PUBLIC

      function getItem(index:integer):TMasterData;
      procedure setItem(const index:integer;const data:TMasterdata);
      Property Items[index:integer]:TMasterData read getItem write setItem;

      procedure setDetail(element:IDetail);
      Property Detail:IDetail write setDetail;

      Procedure NewElement(data:TMasterdata);
      Procedure DeleteElement(data:TMasterdata);
  end;
Ein PKW
Delphi-Quellcode:
uses
  MyInterface;

TYPE
  TPKW = Class(TObject,IDetail)
    PRIVATE
       ffarbe : Tcolor;
       fleistung: Integer;
       fmarke : TPKWMarke;
       fstatus: integer;
       fid : integer;

    PROTECTED
    PUBLISHED
      property Farbe:TColor read ffarbe write ffarbe;
      property Leistung:integer read fleistung write fleistung
      property Marke : TPKWMarke read fmarke write fmarke
      Property ID:integer read fid write fid;
    PUBLIC
     Procedure SetMaster(element:IMaster);

     Property Master:IMaster write setMaster;

     Procedure AddNew();
     Procedure Edit(id:integer);
     procedure Delete(id:integer);

  End;
Ein Hoverboard
Delphi-Quellcode:
uses
  MyInterface;

TYPE
  THoverboard = Class(TObject,IDetail)
    PRIVATE
       fid : integer;
       fstatus : integer;
       fname : string;
       fflughoehe : integer;
       fakkuleistung : double;

    PROTECTED
    PUBLISHED
      Property ID:integer read fid write fid;
      property Status: integer read fstatus write fstatus;
        :
        :
    PUBLIC
     Procedure SetMaster(element:IMaster);
     Property Master:IMaster write setMaster;

     Procedure AddNew();
     Procedure Edit(id:integer);
     procedure Delete(id:integer);

  End;
Nur mal zur Verdeutlichung grob hingezimmert.
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#12

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 17:05
Ok, ich würde das über Observer angehen.

Listboxen können sich z.B. bei einer PKW-Liste registrieren.
Wird etwas in der Liste geändert, werden alle Beobachter informiert und werden sich daraufhin neu zeichnen.
Die Einträge einer Liste wiederum können sich als Beobachter bei einem Datenobjekt eintragen.

Eine Detailview kann sich bei einer Liste anmelden und wird informiert, wenn sich ihr aktueller Eintrag verändert.
Die View kann sich dann das aktuelle Objekt von der Liste abrufen (es muss nicht die ID sein, weil ja ohnehin dort ein Objekt vorliegt) und seinen Inhalt neu zeichnen.

Wird dort ein Wert des Datenobjektes geändert, muss das nicht die View an die Listbox melden weil ja die Listboxitems schon ihr Datenobjekt beobachten und sich bei Änderungsinformationen ohnehin neu zeichnen.

Also müssen i.d.R. die Controls untereinander kaum kommunizieren (außer die Meldung einer Liste an angemeldete Views bei einem Recordwechsel, da die aktuell ausgewählte Recordzeile nichts im Datenmodell zu suchen hat).


Ob Du Die Observerliste über Interfaces oder Objekte (vielleicht mit gemeinsamen Basisklassen) organisierst ist Geschmacksache.
Ich würde jedenfalls die gegenseitige Abhängigkeit der Controls möglichst gering halten.


NACHTRAG:

Umgekehrt muss Detail aber auch mit Master kommunizieren:

Detail sagt Master : Neues Element mit ID abc Status:blau und Text: Wupti eingefügt.
Wenn Du in der Detailview einen Eintrag hinzufügen oder löschen willst, dann muss diese View auch die ObjektListe von der Listbox abrufen. Sie muss aber dann nicht die Listbox informieren, wenn Sie ein Add oder Delete ausführt, da die Liste selbst ihre Beobachter über Änderungen informiert. Ok, für die nachträgliche Synchronisation des aktuellen Records kann eine Abstimmung Sinn machen.
Aber über die Beobachterliste kann man ja alle Interessenten anschauen und entscheiden, welche Informationen diese erhalten sollen.

Interfaces muss man hierbei nicht verwenden. Es bringt ein paar Vorteile, aber auch mehr Aufwand.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli ( 9. Nov 2016 um 17:23 Uhr)
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#13

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 19:02
Öhmmm......ok.....ich hab mal gemalt.

Dabei sind die schwarzen Verbindungen der Datenfluss.
Die grünen mein Ansatz.
Die roten dein Vorschlag.

(Ich hoffe ich hab dich da richtig Verstanden).

Nochmal im Detail

Zitat:
Listboxen können sich z.B. bei einer PKW-Liste registrieren.
Wird etwas in der Liste geändert, werden alle Beobachter informiert und werden sich daraufhin neu zeichnen.
Jo das ist logisch

Zitat:
Die Einträge einer Liste wiederum können sich als Beobachter bei einem Datenobjekt eintragen.
Ähmm.....nö. Wie schon gesagt wird die Liste direkt aus der DB gefüllt/erzeugt, je nach Sort/Filter. Dem zufolge ist die Liste ein Datenobject (soweit ich das verstanden hab)

Zitat:
Eine Detailview kann sich bei einer Liste anmelden und wird informiert, wenn sich ihr aktueller Eintrag verändert. Die View kann sich dann das aktuelle Objekt von der Liste abrufen (es muss nicht die ID sein, weil ja ohnehin dort ein Objekt vorliegt) und seinen Inhalt neu zeichnen.
Öhm, das wär sinnig für den Fall, das der Datensatz der Detailview aufgrund von Filter aus der Liste
rausfliegen würde. Mehr würde da aber auch nicht drüber laufen. Da die Liste nicht alle Daten eines Datensatzes hat, kann die Detailview auch ihre daten nicht von der Liste bekommen. Wär auch nicht grad sinnig, den Hauptspeicher mit den ganzen Datensätzen zu zupflastern.


Zitat:
Wenn Du in der Detailview einen Eintrag hinzufügen oder löschen willst, dann muss diese View auch die ObjektListe von der Listbox abrufen. Sie muss aber dann nicht die Listbox informieren, wenn Sie ein Add oder Delete ausführt, da die Liste selbst ihre Beobachter über Änderungen informiert. Ok, für die nachträgliche Synchronisation des aktuellen Records kann eine Abstimmung Sinn machen.
Aber über die Beobachterliste kann man ja alle Interessenten anschauen und entscheiden, welche Informationen diese erhalten sollen.
Ok...mal sehen ob ich das so richtig verstanden hab.

Die Detailview übergibt die (eingegeben) Daten an ihr Datenobjekt (einzelner, kompletter DS), dieses Informiert seine Beobachter (u.a. die Datenliste) über die Veränderung. Die Datenliste wiederum informiert den Listview, das er sich neu zeichnet.

hmmm....
Miniaturansicht angehängter Grafiken
konzept.jpg  
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#14

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 19:13
Ok..nach dem ich da nochmal in mich gegangen bin hab ich das jetzt glaub ich verstanden (ignorier einfach die roten und grünen Linien...passen nicht).

Aber letztlich würde das Observer-Pattern hier nur die "Richtung" ändern (statt linksrum, rechtsrum )

Aber worum es mir eigentlich ging ist mehr die Implementierung. Eben ob Interfaces oder Messages (via PostMessage/SendMessage), oder wie auch immer.
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#15

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 19:47
Ich verwirre Dich wohl mehr als geplant.

Ich war zuletzt davon ausgegangen, dass Du dem Formular alle Daten in Form von Objekten bzw. Objektlisten bereitstellt.
Wenn das Formular zur Anzeige der Daten nochmal auf die Datenbank zugreifen muss, sieht das natürlich noch etwas anders aus.
Dann kannst Du keine Objekte zur Benutzung übergeben sondern tatsächlich die Datensatz-Ids.

Grundsätzlich haust Du m.E. die Datenschicht und die Formularschicht zu sehr durcheinander.
Versuche doch mal, das noch konsequenter zu trennen. Dafür gibt es verschiedene Möglichkeiten. Am einfachsten, alles was die Datenmanipulation betrifft in eine eigene Unit auslagern.

Dann könntest Du theoretisch alle Änderungen ohne ein Formular durchführen. Z.B. in einer Methode oder über ein Script.
Dann hast Du eine Datenschicht (und Businesslogik), die für sich funktioniert.

Jetzt muss das Formular sich nur noch an diese Unit wenden und sagen, was es gerade braucht.
Dann kannst Du eine PKW-Liste in Form von kompletten Objekten oder als DataSet übergeben.
Auch kannst Du durch Aufruf bestimmter Methoden Änderungen in der Datenschicht veranlassen.

Jetzt muss den Controls noch gesagt werden, was und wie sie sich zeichnen und verhalten sollen.

Dabei solltest Du aber auf die Zuständigkeiten achten. In einer PKW-Klasse hat m.E. AddNew, Edit(IrgedeineID) und Delete(IrgendeineID) nichts verloren.
Der PKW ist nicht dafür zuständig, irgendwelche Listen zu manipulieren. Er ist ein Objekt und fertig.
Wenn der User in der View, die gerade einen PKW anzeigt, einen neuen PKW in die originalliste hinzufügen können soll, dann muss die View auch die Originalliste kennen. Besser sollte aber AddNew und Delete irgendwie über die Listbox selbst ermöglicht werden (ähnlich einem DBNavigator).

Der PKW sollte üblicherweise auch nicht die Liste kennen, in der er enthalten ist - es sei denn, dies wäre aus dem Datenmodell heraus sinnvoll (Sohn muss seinen Vater kennen).

Delphi-Quellcode:
  TPKW = Class(TObject,IDetail)
     PRIVATE
        ffarbe : Tcolor;
        fleistung: Integer;
        fmarke : TPKWMarke;
        fstatus: integer;
        fid : integer;

     PROTECTED
     PUBLISHED
       property Farbe:TColor read ffarbe write ffarbe;
       property Leistung:integer read fleistung write fleistung
       property Marke : TPKWMarke read fmarke write fmarke
       Property ID:integer read fid write fid;
     PUBLIC
      Procedure SetMaster(element:IMaster);

      Property Master:IMaster write setMaster;

      Procedure AddNew(); // ???
      Procedure Edit(id:integer); // ???
      procedure Delete(id:integer); // ???

   End;

Ich denke, wenn Du Dich erst konkret um das Daten- und Geschäftsmodell kümmerst und erst als nächsten Schritt um die GUI wird es übersichtlicher.


Aber mal sehen, vielleicht kann ja noch jemand anders besser helfen.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli ( 9. Nov 2016 um 22:20 Uhr)
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.093 Beiträge
 
Delphi 12 Athens
 
#16

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 20:02
Also ich würde das mit Messages machen, aber nicht die Windows-Versionen, die haben zuviel Programmier-Overhead.
Schau dir mal TMassage an,
ich weiss nur nicht ob du TurboDelphi hast, könnte sein das es da noch nicht drin ist.
Die sind schlank, einfach zu handhaben, arbeiten mit Generics und anonymen Methoden und erfüllen ihren Zweck 100%.

Die Messages finde ich mittlerweile viel handlicher als alles andere, weil man mit ihnen auch perfekt die Module enkoppeln kann.
Selbst mit Interfaces hatte ich schon "Segmentation fault" gesehen, allerdings unter Android.
Wenn du nur bei Windows bleibst ist aber wohl beides OK, aber trotzdem TMessage<TXyz> hat schon was ...

Rollo
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#17

AW: Konzeptfrage: Interface oder Messages

  Alt 9. Nov 2016, 20:36
@Rollo

Nein, TD kennt das so nicht (schon gleich nicht mit Generics)

Hab mir mal trotzdem den Wiki-Eintrag angeschaut und letztlich ists nicht viel anders
als PostMessage/SendMessage (wenn auch um einiges bequemer ).

Ein zentraler Manager (Messageloop) nimmt nachrichten von registierten sendern entgegen und
leitet sie an die registrieren empfänger weiter
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#18

AW: Konzeptfrage: Interface oder Messages

  Alt 10. Nov 2016, 10:03
Ich verwirre Dich wohl mehr als geplant.

Ich war zuletzt davon ausgegangen, dass Du dem Formular alle Daten in Form von Objekten bzw. Objektlisten bereitstellt.
Wenn das Formular zur Anzeige der Daten nochmal auf die Datenbank zugreifen muss, sieht das natürlich noch etwas anders aus.
Dann kannst Du keine Objekte zur Benutzung übergeben sondern tatsächlich die Datensatz-Ids.

Grundsätzlich haust Du m.E. die Datenschicht und die Formularschicht zu sehr durcheinander.
Versuche doch mal, das noch konsequenter zu trennen. Dafür gibt es verschiedene Möglichkeiten. Am einfachsten, alles was die Datenmanipulation betrifft in eine eigene Unit auslagern.

Dann könntest Du theoretisch alle Änderungen ohne ein Formular durchführen. Z.B. in einer Methode oder über ein Script.
Dann hast Du eine Datenschicht (und Businesslogik), die für sich funktioniert.

Jetzt muss das Formular sich nur noch an diese Unit wenden und sagen, was es gerade braucht.
Dann kannst Du eine PKW-Liste in Form von kompletten Objekten oder als DataSet übergeben.
Auch kannst Du durch Aufruf bestimmter Methoden Änderungen in der Datenschicht veranlassen.
Ähm...die Trennung ist schon da

Die Listview wendet sich an das Listen-Daten-Objekt um Daten für die Anzeige zu bekomen. Genauso wie sich die Detailview sich an ihr Datensatz-Daten-Objekt wendet um Daten zu bekommen bzw Daten zu
manipulieren (vgl. Schaubild).

1. Kommunikations-Weg ohne Observer bei Änderung eines Datensatzes durch Detailview

User änder Daten in Detailview -> Detailview schickt die Daten an Datensatz-Daten-Objekt(DDO) ->
DDO ändert die Datenbank -> Detailview schickt nachricht an Listview -> Listview fragt Listen-Daten-Objekt (LDO) nach frischen Daten -> LDO aktuallisiert seine Daten von der DB ->Listview zeichnet sich neu.

2. Kommunikations-Weg mit Observer (so wie ich dich Verstanden hab):

a)Registrierung

LDO registriert sich bei DDO als Listener, die Listview registriert sich als Listener bei der LDO
(Den Rückweg lass ich mal der einfachheithalber außer Acht)

b) Der Weg

Wie unter 1. ändert der User die Detailview -> diese sagt dem DDO, das es die Daten ändern soll ->
DDO änder die Daten in der DB -> DDO schickt Nachrichten an seine Listener ->LDO bekommt nachricht und aktuallisiert seine Daten aus der DB->Benachrichtigt seine Listener, das es sich geändert hat->Listview zeichnet sich neu

Der Unterschied zwischen 1 und 2 ist also, das nicht die UI untereinander kommuniziert (wie bei 1), sonder die Datenobjekte untereinander (was nach etwas Überlegung auch mehr Sinn ergibt).
Miniaturansicht angehängter Grafiken
aufbau.jpg  
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#19

AW: Konzeptfrage: Interface oder Messages

  Alt 10. Nov 2016, 11:07
Ja genau.

So wird das übersichtlicher.

Die Listbox interessiert sich, wenn sich etwas an der Datenliste ändert.
Also muss sie eine Info aus der Datenschicht bekommen: "Datenliste geändert".

Die DetailView interessiert sich für ein Datenobjekt und muss dazu eine Änderungsmeldung erhalten.
Aber die Listbox muss zusätzlich ihre DetailView kennen um sie zu beauftragen, jetzt ein anderes Datenobjekt anzuzeigen. Wenn jede Listbox genau eine DetailView hat, kann sie direkt die Instanz der View kennen. Ansonsten kann sie mehrere Views in einer Liste verwalten und nacheinander ansprechen oder sie kann einen Rundruf in die Welt senden und alle Views entscheiden selbst, ob sie darauf reagieren.
Diese Nachricht einer Listbox an ihre DetailView(s) ist aber m.E. die einzige notwendige Kommunikation zwischen den Controls.
Alles andere sollte über die Datenschicht kommuniziert werden.

Dazu kann wiederum jedes Datenobjekt alle seine zuständigen Controls kennen und denen eine Änderungsnachricht schicken (Observer) oder die Datenschicht setzt ein Flag DataIsInvalidate und das Formular prüft zyklisch (in einem Timer oder in OnIdle), ob das gesetzt ist und veranlasst seine Controls, sich neu zu zeichnen.
Beides hat Vor- und Nachteile.

Jedenfalls sollte aus der DetailView heraus nach eine Datenänderung nicht noch eine Listbox oder ein anderes Control über die Änderung informiert werden müssen.
Sonst wird vielleicht irgendwann einmal durch einen Job eine automatische Änderung der Daten durchgeführt und Du weißt gar nicht, welche Controls diese Daten anzeigen und welche Du jetzt eigentlich informieren musst weil die Änderung ja jetzt nicht in einer View veranlasst wurde.

In Deiner Skizze hast Du jetzt quasi schon einen ORM dargestellt.
Alle Zugriffe der Controls werden über Datenobjekte gemappt. Vorher hattest Du mal erwähnt, dass die Objekte nicht alle Datenbestandteile beinhalten und die GUI deswegen noch Daten aus der Datenbank holen müssen.
In dem Fall wäre Deine Skizze so noch nicht korrekt.

Also Du musst Dir überlegen, ob sämtliche Zugriffe auf die Daten über Objekte erfolgen sollen oder ob sich das Formular auch noch Daten aus der Datenbank holen muss.
Davon ist dann auch nochmal abhängig, wie man das Projekt organisiert.

Wenn Du nicht alles vollständig über Daten-Objekte handelst kann man sich auch überlegen, auf Objekte und Interfaces zu verzichten und die Datenzugriffe über Querys auf die DB zu organisieren.


Wichtigstes Fazit aus meiner Sicht: Kommunikation zwischen Controls nur, soweit sie sich auf die GUI-Logik bezieht (abhängige Controls bei einem Datensatzwechsel informieren).
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli (10. Nov 2016 um 11:36 Uhr)
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#20

AW: Konzeptfrage: Interface oder Messages

  Alt 10. Nov 2016, 14:35


Vorher hattest Du mal erwähnt, dass die Objekte nicht alle Datenbestandteile beinhalten und die GUI deswegen noch Daten aus der Datenbank holen müssen.
DAS war das Missverständnis

Was ich meinte war eigentlich nur das in der LDO (und damit auch im Listview) nur die
Daten vorgehalten werden, die zur Anzeige notwendig sind (also z.B. ID und Marke vom PKW),
dafür aber mehrer Sätze, wärend im DDO (und damit in der Detailview) nur ein satz vorgehalten
wird (der der zur Anzeige/Änderung notwendige), dafür aber mit allen feldern des DS (also id,marke,farbe,leistung usw...eines PKW)

Im Moment spiel ich grad ein bischen mit dem Observer-Pattern rum und versucht mich in einem Testprojekt damit. Leider gelingt mir grad die Abstraktion nicht so ganz (in den Basis-Klassen kenn ich ja die konkreten Daten, die bei einer Notification übergeben werden noch garnicht).
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:35 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz