AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi MVC - Wie korrekt zweites Fenster instanzieren?
Thema durchsuchen
Ansicht
Themen-Optionen

MVC - Wie korrekt zweites Fenster instanzieren?

Ein Thema von RSE · begonnen am 9. Aug 2012 · letzter Beitrag vom 9. Aug 2012
Antwort Antwort
RSE

Registriert seit: 26. Mär 2010
254 Beiträge
 
Delphi XE Enterprise
 
#1

MVC - Wie korrekt zweites Fenster instanzieren?

  Alt 9. Aug 2012, 12:02
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:
  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;
Für die UI gibt es eine Basisklasse und davon abgeleitet verschiedene spezielle Fenster:
Delphi-Quellcode:
  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;
Und dann gibt es eine Klasse TLoader, die momentan ein Ladefenster zu Programmstart zeigt und das Hauptfenster und dessen Logik instanziert:
Delphi-Quellcode:
  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;
(Quelltext wurde gekürzt)
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?
"Seit er seinen neuen Computer hat, löst er alle seine Probleme, die er vorher nicht hatte."

Geändert von RSE ( 9. Aug 2012 um 12:52 Uhr)
  Mit Zitat antworten Zitat
RSE

Registriert seit: 26. Mär 2010
254 Beiträge
 
Delphi XE Enterprise
 
#2

AW: MVC - Wie korrekt zweites Fenster instanzieren?

  Alt 9. Aug 2012, 15:25
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:
  • Die UI darf die konkrete Implementierung der Logik nicht kennen (deshalb nutze ich die abstrakte Klasse TMainIntf als Interfacedefinition)
  • Die UI darf den Controller nicht kennen
  • Die Logik darf weder den Controller noch die UI kennen
  • Der Controller verwaltet alles
Nun fällt mir nur eine Lösung meines Problems ein: Ich gebe der Logik Wissen über den Controller. Damit ist folgendes machbar:
Für die Logik der Projekte gibt es folgende Umsetzung:
Delphi-Quellcode:
  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 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.
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?
"Seit er seinen neuen Computer hat, löst er alle seine Probleme, die er vorher nicht hatte."
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.453 Beiträge
 
Delphi 12 Athens
 
#3

AW: MVC - Wie korrekt zweites Fenster instanzieren?

  Alt 9. Aug 2012, 15:54
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.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: MVC - Wie korrekt zweites Fenster instanzieren?

  Alt 9. Aug 2012, 16:18
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:
  • 1. Die UI darf die konkrete Implementierung der Logik nicht kennen (deshalb nutze ich die abstrakte Klasse TMainIntf als Interfacedefinition)
    2. Die UI darf den Controller nicht kennen
    3. Die Logik darf weder den Controller noch die UI kennen
    4. Der Controller verwaltet alles
Ich stecke jetzt nicht so im Thema drin, bin auch nur interessierter Laie in der Hinsicht (muss altes Programm pflegen, wo sowas sehr selten eingesetzt wird).
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:
http://openbook.galileocomputing.de/...tel_08_002.htm

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?
  Mit Zitat antworten Zitat
RSE

Registriert seit: 26. Mär 2010
254 Beiträge
 
Delphi XE Enterprise
 
#5

AW: MVC - Wie korrekt zweites Fenster instanzieren?

  Alt 9. Aug 2012, 17:43
Ich habe gerade folgendes gefunden und gemerkt, dass mein Verständnis von MVC doch sehr lückenhaft war:
http://codebetter.com/jeremymiller/2...e-of-contents/
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.

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.
Bevor ich jetzt noch einen Umweg mehr einbaue (Interfaces/Events) und damit den Aufwand weiter erhöhe, ist in meinem Fall wohl eine Referenz weniger kritisch.

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.
Der Vorteil von Interfaces gegenüber abstrakten Klassen liegt m.E. in der Referenzzählung und dass eine Klasse mehrere Interfaces implementieren kann. Beides brauche ich nicht. Da die abstrakte Klasse direkt zur konkreten Implementierung gehört, brauche ich gar nicht auf den Gedanken zu kommen in der abstrakten Klasse etwas implementieren zu wollen. Das kann in jedem Fall in der konkreten Ableitung passieren.

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.
"Seit er seinen neuen Computer hat, löst er alle seine Probleme, die er vorher nicht hatte."

Geändert von RSE ( 9. Aug 2012 um 17:45 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


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 03:58 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