Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Trennung von GUI und Logik (https://www.delphipraxis.net/200525-trennung-von-gui-und-logik.html)

Gausi 29. Apr 2019 12:44

AW: Trennung von GUI und Logik
 
Warum der Umweg über eine separate StringList mit Assign? Man kann der Datenklasse doch auch direkt das Memo (bzw. dessen Property Strings) übergeben.

Delphi-Quellcode:
type

  TData = class
    public
        procedure FillList(aStrings: tStrings);
  end;

  TForm2 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
    aData: TData;
  public
    { Public-Deklarationen }
  end;



var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TData }

procedure TData.FillList(aStrings: tStrings);
begin
    aStrings.Add('eins');
    aStrings.Add('zwei');
    aStrings.Add('drei');
    aStrings.Add('vier');
    aStrings.Add('fünf');
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
    aData := TData.Create;
    aData.FillList(Memo1.Lines);
end;
Funktioniert wunderbar, und man kann die Methode FillList auch für andere Zwecke nutzen, wenn man z.B. die Daten nicht in einem Memo haben will.

Für Fortschrittsanzeigen etc. könnte man z.B. mit Messages arbeiten (der Datenklasse übergibt man dann einmalig das Handle der MainForm, die die Nachrichten dann abarbeitet), oder man erstellt eigene Events mit passenden EventHandlern. Dann hat man im Code der Datenklasse regelmäßig etwas wie
Delphi-Quellcode:
if assigned(fOnProgress) then
    fOnProgress(param1, param2, param3, ...);
wobei fOnProgress eine Variable vom Typ Procedure( parameterliste ) of Object ist, die in der MainForm implementiert ist und entsprechend zugewiesen wird.

generic 30. Apr 2019 06:58

AW: Trennung von GUI und Logik
 
Na dann ergänze ich mal paar Ideen. Wirkt gerade alles sehr kompliziert.

Wo wir uns einige sind:
Die Logik darf das Oberfläche nicht kennen und muss somit auch ohne Funktionieren.
Die Oberfläche interessiert sich aber für neue und geänderte Daten, damit diese ggf. Dargestellt werden können.
Die Geschäftsobjekte sollte es nun an einer Stelle im Quelltext geben. "single point of truth"

Daher werfe ich mal eine DIY Lösung ein, welche mit Ereignissen arbeitet, welche die Logik zur Verfügung stellt.
Der klassischen Ansatz "Observer Pattern".
Vielleicht auch das "Publisher-Subscriber Pattern".

Siehe auch:
https://github.com/spinettaro/delphi-event-bus
https://www.danielespinetti.it/2017/...-and-mvvm.html

p80286 30. Apr 2019 10:20

AW: Trennung von GUI und Logik
 
Zitat:

Zitat von generic (Beitrag 1431324)
Die Logik darf das Oberfläche nicht kennen und muss somit auch ohne Funktionieren.

"ohne" übersetz ich mal mit "beliebig (Minimal)".
Dazu dann eine dumme(?) Frage:
Gegeben sei eine Logik die mit Hilfe von Threads irgendwelche Daten bearbeitet.Innerhalb des Treads wird regelmäßig ein Postmessage mit dem aktuellen Stand der Bearbeitung abgesetzt.
Was passiert, wenn die GUI diese Messages nicht kennt?

Gruß
K-H

haentschman 30. Apr 2019 10:32

AW: Trennung von GUI und Logik
 
Zitat:

Was passiert, wenn die GUI diese Messages nicht kennt?
Die Messages die der Thread (der in der Logik erzeugt wird) sendet, kommen in der Logik an. Die Logik macht ein Event draus, was die GUI versteht. Die Logik sollte dann auch der Puffer sein, wenn die Aktualisierungen zu schnell kommen und die GUI die einfach nicht darstellen kann. :thumb: ...fertsch.
Zitat:

"ohne" übersetz ich mal mit "beliebig (Minimal)".
"ohne" heißt "ohne". Imho darf die Logik die GUI nicht kennen. Beispiel: Wenn in der Logik Daten verarbeitet werden und schon in der Logik "Rücksicht" auf die Darstellung genommen wird stellen sich mir die Nägel hoch...:lol:

DeddyH 30. Apr 2019 11:06

AW: Trennung von GUI und Logik
 
Seitdem ich viel mit Angular und RxJS mache weiß ich die Eleganz des Observer-Patterns erst richtig zu schätzen. Allerdings ist dort die GUI auch threadsafe, da müsste man in Delphi halt etwas aufpassen, machbar ist das aber auch.

TurboMagic 1. Mai 2019 08:32

AW: Trennung von GUI und Logik
 
Zitat:

Zitat von hum4n0id3 (Beitrag 1431267)
Na wunderbar! Danke für das Kommentar :)
Und ich denke auch das ich ein Memory Leak erzeuge, jedenfalls nach meinem bisherigem Verständnis. Das TStringList, nach meinem Verständnis, wird nicht (sauber) geleert. Vermutlich nur das DesktopFile. Meine bisherigen Bemühungen schlugen aber bisher Fehl. Aber dafür mache ich mal bei Gelegenheit einen eigenen Thread auf.

Danke! Wieder was gelernt! Tolles Forum.

MfG

Ob es wirklich ein memory Leak ist seht ihr nicht, wenn das grüne Licht an geht, sondern wenn ihr in die dpr des Projektes
als erste Anweisung nach dem begin das hier reinschreibt:

ShowMemoryLeaksOnSHutdown := true;

Dann Programm ausführen, die entsprechende Aktion aufrufen und Programm beenden. Kommt keine Meldungsbox beim Beeenden
gibt's kein leak, kommt eine ist eines vorhanden.

Maekkelrajter 1. Mai 2019 15:17

AW: Trennung von GUI und Logik
 
Hallo,

seit ein paar Tagen bin ich nun dabei, die hier vorgeschlagenen Methoden der Kommunikation zwischen GUI und Logik auszuprobieren. Dabei wird für mich immer deutlicher, dass eine absolute Trennung garnicht möglich ist, wenn die Logik während der Abarbeitung des Auftrages, der vom GUI erteilt wurde, Informationen an das GUI übermitteln bzw. Aktionen des GUI auslösen soll. Die absolute Trennung funktioniert doch eigentlich nur, wenn ausschließlich der Rückgabewert einer Funktion ausgewertet werden soll.
Denn sowohl beim Absetzen von Windows-Messages als auch beim Feuern von Events muss die Logik doch wissen, wohin 'gezielt' werden soll, um eine bestimmte Aktion des GUI auszulösen. Wie soll das gehen, wenn Logik und GUI überhaupt nichts voneinander 'wissen'?
Hier meine Implementierung eines Update - Events, das tadellos funktioniert.

Die Deklaration in der Logik-Klasse sieht folgendermaßen aus:

Delphi-Quellcode:
type

TUpdateGUIEvent = procedure(Sender: TObject; index:Integer; s: string) of Object;


TGlExLogic = Class
  private
    FUpdateGUI : TUpdateGUIEvent;
    Procedure UpdateGUI(Index:Integer; s: string);
    [...]
  public
    property OnUpdateGUI: TUpdateGUIEvent read FUpdateGUI write FUpdateGUI;
End;
So wird das Event in der Logik-Klasse ausgelöst:

Delphi-Quellcode:
Procedure TGlExLogic.UpdateGUI(Index:Integer; s :string);
begin
  If Assigned(FUpdateGUI) then
  begin
     FUpdateGUI(Self, Index, s);
  end;
end;
Die Implementierung im GUI sieht dann so aus:

Delphi-Quellcode:

procedure TGL4Mainform.FormCreate(Sender: TObject);
begin
   GLMainLogic := TGlExLogic.create(self);
   GlMainLogic.OnUpdateGUI := UpDateGUI;
end;

Procedure TGL4Mainform.UpdateGUI(sender:TObject; Index:Integer; s :string);
begin
  case index of
     1: Memo1.Lines.Add(s);
     2: Memo2.Lines.Add(s);
     8: label8.caption := s;
      [...] usw.
  end;
end;
Die Logik muss doch z. B.wissen, welche Parameter mitgegeben werden müssen, um bei einem bestimmten Control der Mainform ein Update auszuführen?! Es ist dann aber auch nie auszuschließen, das bei Änderung des GUI das eine oder andere nicht mehr funktioniert.
Da wird man wohl notgedrungen Kompromisse machen und von der reinen Lehre der OOP abweichen müssen. Oder habe ich da irgendwas nicht verstanden? :?

Gruß LP

PS: Die oder Das GUI? Nach den geltenden Regeln heißt es doch im Deutschen das Interface? Oder doch nicht??

Klaus01 1. Mai 2019 17:02

AW: Trennung von GUI und Logik
 
.. die Oberfläche muss doch wissen, welches Element es updaten muss.
Das hat nicht zwingend etwas mit dem Wissen der Businesslogik zu tun.
Wie das Ergebnis zustande kam, welches die Oberfläche aktualisieren muss
darüber weiß die Oberfläche nichts.

Ich weiß nicht, ob es nicht vielleicht sinnvoller wäre ein Ereignis pro GUI Element
zu haben?

Grüße
Klaus

DeddyH 1. Mai 2019 17:11

AW: Trennung von GUI und Logik
 
Die Logikschicht muss oder gar darf von der Oberfläche nichts wissen. Im Übrigen kann es ihr ja auch egal sein, ob ein String jetzt in einem Label, einem Edit oder als ListBox-Item dargestellt wird. Sie stellt einfach nur Ereignisse zur Verfügung, an die sich die Darstellungsschicht einklinken kann oder auch nicht.

p80286 1. Mai 2019 17:22

AW: Trennung von GUI und Logik
 
Zitat:

Zitat von haentschman (Beitrag 1431343)
Zitat:

Was passiert, wenn die GUI diese Messages nicht kennt?
Die Messages die der Thread (der in der Logik erzeugt wird) sendet, kommen in der Logik an. Die Logik macht ein Event draus, was die GUI versteht. Die Logik sollte dann auch der Puffer sein, wenn die Aktualisierungen zu schnell kommen und die GUI die einfach nicht darstellen kann. :thumb: ...fertsch.

Zitat:

Zitat von DeddyH (Beitrag 1431413)
Die Logikschicht muss oder gar darf von der Oberfläche nichts wissen. Im Übrigen kann es ihr ja auch egal sein, ob ein String jetzt in einem Label, einem Edit oder als ListBox-Item dargestellt wird. Sie stellt einfach nur Ereignisse zur Verfügung, an die sich die Darstellungsschicht einklinken kann oder auch nicht.

:wiejetzt:
Muß die Logik sich darum kümmern was aus ihren Messages wird oder nicht?

Gruß
K-H


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:49 Uhr.
Seite 2 von 3     12 3      

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