![]() |
MVC - Wie korrekt zweites Fenster instanzieren?
Hallo,
ich implementiere gerade erstmals eine leichtgewichtige Trennung von Logik und UI. Diese funktioniert bei mir folgendermaßen: Die Logik ist gekapselt. Jede spezielle Logik-Klasse leitet sich von einer abstrakten Klasse ab, die das Interface zur Benutzung definiert. Diese abstrakte Klasse leitet sich von einer Basisklasse für alle Logiken ab.
Delphi-Quellcode:
Für die UI gibt es eine Basisklasse und davon abgeleitet verschiedene spezielle Fenster:
TLogicIntf = class(TObject)
// Basisklasse für alle Logiken end; TMainIntf = class(TLogicIntf) // abstrakte Basisklasse für die MainLogic, welche das Interface für die UI definiert public procedure Foo; virtual; abstract; end; TMain = class(TMainIntf) // konkrete Implementierung der MainLogic public procedure Foo; override; end;
Delphi-Quellcode:
Und dann gibt es eine Klasse TLoader, die momentan ein Ladefenster zu Programmstart zeigt und das Hauptfenster und dessen Logik instanziert:
TView = class(TForm)
// Basisklasse für alle Views strict private FRestorePos: Boolean; FRestoreSize: Boolean; public // Konstruktoren und Destruktoren constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property RestorePos: Boolean read FRestorePos write FRestorePos default True; property RestoreSize: Boolean read FRestoreSize write FRestoreSize default True; end; TMainForm = class(TView) // erste spezielle View strict private FMainLogic: TMainIntf; strict protected property MainLogic: TMainIntf read FMainLogic; public constructor Create(AOwner: TComponent; AMainLogic: TMainIntf); reintroduce; end;
Delphi-Quellcode:
(Quelltext wurde gekürzt)
TLoader = class(TForm)
LoadLbl: TLabel; strict private FMainForm: TView; FMainLogic: TLogicIntf; FWindowsInitialized: Boolean; protected procedure DoShow; override; end; procedure TLoader.DoShow; begin inherited; if not FWindowsInitialized then // TAsync.Call: Die anonyme Methode wird aufgerufen, nachdem eine asynchron gesendete Windows-Message angekommen ist, also wenn TLoader tatsächlich sichtbar ist. TAsync.Call(procedure var Handler: TEvtPropBool.TChangedEvt; begin if FWindowsInitialized then Exit; FWindowsInitialized := True; LoadLbl.Caption := 'Laden...'; Application.ProcessMessages; FMainLogic := TMain.Create; FMainForm := TMainForm.Create(Application, TMainIntf(FMainLogic)); FMainForm.Show; Hide; end); end; Bis zur Erstellung des Hauptfensters meiner Anwendung ist also alles klar und ich bin glücklich mit der Vorgehensweise. Da ich aber keinerlei Erfahrungen mit MVC oder ähnlichem habe, stellt sich nun folgende Frage: Unser Programm stellt im Hauptfenster eine Auswahl an Projekten bereit, mit denen gearbeitet werden kann. Jedes Projekt soll in einem eigenen Fenster angezeigt werden. In die UI (TMainForm) gehört m.E. nur der ButtonClick, der zu dem Projekt gehört, was geöffnet werden soll. Im OnClick wird dann eine Methode der Logik aufgerufen, die das Projekt initialisiert etc. und letztlich auch das Öffnen des neuen Fensters anstoßen sollte. Die Logik kennt natürlich weder die UI noch den Loader (Controller). Wie funktioniert das Öffnen des neuen Fensters bei solchen Trennungen üblicherweise? |
AW: MVC - Wie korrekt zweites Fenster instanzieren?
Ich weiß nicht, ob hier keiner antwortet, weil die Frage zu schwer ist, zu viel zu lesen ist, oder weil ich Google mehr quälen soll...
Ich werfe mal eine Möglichkeit in den Raum, die mir in den Sinn kommt und den Grund, warum ich das nicht einfach so umsetze ohne zu fragen: Ich habe bereits einiges zur Trennung vom UI und Logik gegoogelt und gelesen, ohne mir im einzelnen aufzuschreiben wo das war. Darunter war auch eine kernige Aussage, die folgende Bedingungen forderte:
Für die Logik der Projekte gibt es folgende Umsetzung:
Delphi-Quellcode:
Wenn TMain meint ein Projekt öffnen zu müssen, wird der Controller beauftragt, Objekte für die Logik und die View zu instanzieren. Bei diesem Aufruf wird die Logik-Instanz des Projekts als TProjIntf zurückgegeben. TMain initialisiert nun die ProjektLogik. Nach Abschluss der Initialisierung wird über einen Event der Projektlogik die Projektview angezeigt. TMain ist nun für die Freigabe der ProjektLogik verantwortlich.
TProjIntf = class(TLogicIntf)
// Basisklasse für die Logik aller Projekte strict protected FProjectID: Integer; procedure SetProjectID(Value: Integer); virtual; public property ProjectID: Integer read FProjectID write SetProjectID; end; TProjXYIntf = class(TProjIntf) // abstrakte Basisklasse für die Logik des Projekts XY, welche das Interface für die UI definiert end; TProjXY = class(TProjXYIntf) // konkrete Implementierung der Logik des Projekts XY end; Wenn das Projektfenster geschlossen wird, informiert die View des Projekts die Logik. Diese informiert TMain und TMain gibt das Objekt frei und weiß, dass das Fenster geschlossen ist. Ist das eine sinnige Struktur, oder gibt es da schon wieder zu viele Abhängigkeiten, die aus irgendwelchen Gründen nicht sein sollten? |
AW: MVC - Wie korrekt zweites Fenster instanzieren?
Wenn der Controller auf bestimmte Ereignisse der Logik oder der UI reagieren soll, dann kann man das (wie der Name schon sagt) über entsprechende Events oder ein Observer Pattern lösen. Oder man gibt entsprechende Callback-Interfaces an die Logik bzw. die UI. Damit bleibt die Trennung weiterhin erhalten bzw. das gegenseitige "Kennen" wird auf das notwendigste Minimum (Events, Interfaces) beschränkt.
|
AW: MVC - Wie korrekt zweites Fenster instanzieren?
Zitat:
Aber so wie ich es verstehe ist die Oberfläche der VCL sowohl View als auch Controller (durch die Event-Handler: OnClick, OnMouseOver, OnChange...etc.) und es daher keine strikte Trennung in Delphi dafür geben kann. Schau mal hier, speziell der Punkt Model-View-Presenter weiter unten im Kapitel 8.2.5, vielleicht hilft dir das weiter: ![]() 1. Die UI darf die konkrete Implementierung der Logik nicht kennen: Wäre es nicht besser statt einer abstrakten Klasse ein oder mehrere voneinander abgeleitete Interfaces zu nehmen? Sonst kommt man irgendwann doch noch dazu, in der abstrakten Klasse Code zu implementieren. Durch ein Interface sagst du nur, was für ein Verhalten die zu erstellende Klasse können muss. 2. Die UI darf den Controller nicht kennen: Wenn nur der Controller die UI und Logik kennt (aber nicht anderesrum), wie kommen dann Eingaben (Click, Strings aus Edits usw.) vom UI in den Controller? Eventhandler des Controllers im Objekt-Inspektor den entsprechenden Ereigniss der visuellen Komponente zuweisen? |
AW: MVC - Wie korrekt zweites Fenster instanzieren?
Ich habe gerade folgendes gefunden und gemerkt, dass mein Verständnis von MVC doch sehr lückenhaft war:
![]() Mit dieser sehr guten Erklärung muss ich folgendes zu meiner Umsetzung sagen: Ich habe nicht vor in die 3 MVC-Teile zu splitten, sondern nur in 2 - View und Controller sind in meinen TView-Descendents zusammengefasst. Model ist meine Logik. Mein Loader ist tatsächlich nur zum Instanzieren gedacht und macht sonst nichts weiter. Es handelt sich bei dem Programm um eine Eigenentwicklung mit ausschließlicher in-House Verwendung. TMain muss Basisfunktionalität und eine Auswahl von aktuellen Projekten bereitstellen. Die Projekte werden eine gemeinsame Basis und dann immer wieder neu zu programmierende Inhalte haben. Die Programmierung dieser Projektinhalte muss in kürzester Zeit möglich sein. Änderungen an bestehenden Teilen sind fast ausschließlich Änderungen an der Funktion, die ggf. bis zur UI durchschlagen. Es wird aber z.B. keine Änderung der UI ohne neue Funktionalität geben. Mit ganz viel Vorausschau und Phantasie könnte es vielleicht irgendwann eine Weboberfläche geben. Dann müsste der Controller-Teil sowieso mit erneuert werden. Der Vorteil, den ich mir durch die Trennung verspreche, ist, dass das Design der Logik unabhängig von der UI erfolgen kann. Somit bin ich bei der Strukturierung der Logik nicht mehr an die Aufteilung in Fenster und Frames gebunden. Wenn Informationen aus mehreren logischen Bestandteilen benötigt werden, werden eben mehrere Logikbausteine an eine View übergeben. Zitat:
Zitat:
Ich denke ich werde es so machen wie in meinem vorherigen Post beschrieben. Falls jemand erkennt, dass ich mir mit dem beschriebenen Design entscheidende Probleme einbaue, dann bitte ich darum, mich darauf aufmerksam zu machen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:30 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz