Hallo zusammen,
die von @Yanbel angestoßene Diskussion bzgl. MVVM@WinForms hat mich zum Schreiben dieses Threads geführt. Ich möchte explizit seinem geplanten Thread nicht vorgreifen. Da ich allerdings keine Ahnung habe, was kommt, ich aber auch nicht weiß, wann es kommt, folgt hier eine kleine WinForms-Architekturdesignentwicklung, die der ein oder andere, der vielleicht genauso fachfremd wie ich ist, als Anleitung sehen könnte, etwas besser sein eigenes Projekt zu strukturieren oder neue Möglichkeiten gezeigt zu bekommen. Dazu habe ich erstmal fünf Entwicklungsstufen eines Projekts erzeugt. Für Pros und alte Hasen dürfte dieser Thread aber wahrscheinlich wenig interessant sein.
Das eigentliche Projekt, welches in seinen 5 Stufen hier beschrieben wird, befindet sich als Gesamtpaket im Anhang und ist an sich lächerlich. Es ist ein einfaches Pseudowürfelspiel gegen den Computer und bedarf keiner Erklärung. Auch die verwendete Lade-/Speichertechnik für die Statistik ist ein Witz. Aber genau dabei soll es auch bleiben, um den Fokus auf den Umgang mit dem Formular (im Weiteren kurz das(!) Form, denn das ist auch der englische Begriff für Formular, daher WinForms) an sich zu behalten.
Stufe 1 - Einsteigerart
So würde es wohl ein Programmiereinsteiger machen.
Das Form weiß alles. Alle Variablen sind formweit bekannt. Keine Klasse außer Form. Die ganzen Abläufe sind in den EventHandlern der Buttons untergebracht.
Vorteile
Nachteile
Stufe 2 - Klumpenform
Immer noch ist das Form der Oberboss. Es hat zumindest mal eine weitere Klasse in den Code geschafft, auch wenn es nur minimal besser als Stufe 1 ist.
Vorteil
Nachteile
Stufe 3 - Klassennutzung
Die erste Klasse wurde stark ausgebaut, die Spielmechanik (alias Business Logic?) steckt nun in ihr. Auch das Speichern und Laden ist in eine eigene Klasse gewandert, wurde also vom Rest abgespaltet.
Ich habe auch absichtlich Aufrufe der Art EventHandler->Integration->Operation drin. Im
Was soll der Mumpitz?
Ich teile gern meine Forms in mehrere Partial files auf: EventHandler, Integration, Operation und andere
Dadurch kann ich mir schneller eine Geschichte erzählen lassen: Was kann der User u.a. machen (EventHandler), was passiert dann (Integration), und wenn ich an den Details interessiert bin, also wie dann etwas passiert, dann schau ich mir die Operation-Datei an.
Während Integration bei mir nur formeigene Funktionsaufrufe enthält, werden in Operation andere Klasseninstanzen zur Mitarbeit aufgefordert und man verlässt quasi das formeigene Gebiet.
Zur Übersicht habe ich allerdings keine eigenen partial files in den Projekten erzeugt.
Vorteile
Nachteile
Stufe 4.1 – MVP Oberbossform
Keine Ahnung, ob die Stufe 4 das ist, was man als eine Art von Model-View-Presenter bezeichnet, aber es klingt ein wenig nach dem (nicht vollständig! das Model weiß bei mir nix von ViewModel und Form!), was man Martin Fowler unter der Bezeichnung Supervising Controller zuschreibt, glaubt man dem Wikipedia-Eintrag.
Das Form bleibt weiterhin Oberboss, erzeugt aber eine ViewModel-Instanz, welches sich um den Kontakt zur Spielmechanik kümmert. Das Form selber sendet Befehle ans ViewModel, welches diese dann umsetzt. Werden GUI-Updates benötigt, teilt das ViewModel dies über Property-Änderungen und dem EventRaising von PropertyChanged (als Implementierung der INotifyPropertyChanged-Schnittstelle) mit, wodurch eine BindingSource auf dem Form dies an die einzelnen GUI-Elemente weitergibt. Für jede GUI-Property-Änderung wird also quasi eine Klassenproperty im ViewModel benötigt. Will man auch z.B. Texteingaben aus einer TextBox ans ViewModel weiterleiten, muss man natürlich die Private Setters in Public Setters umwandeln.
Vorteile
Nachteile
Stufe 4.2 – MVP Passivform
In dieser Ausbaustufe wird das Form komplett zur Anzeige degradiert. Das ViewModel übernimmt die Änderungen am GUI. Dies wird dadurch erreicht, dass man in den Projekteigenschaften das Anwendungsframework deaktiviert, Sub Main als Startprozedur einstellt, ein Modul erstellt und dort eine Sub Main definiert, die das ViewModel als Oberklasse bestimmt, welches wiederum das Form aufruft.
Vorteile
Nachteile
Wie geht’s weiter? Ich habe keine Ahnung, da ich Stufe 4 noch nie praktisch außerhalb des hiesigen Demoprojektes angewendet habe. Jetzt sind Eure Erfahrungen und Tipps gefragt. Zweifellos kommen zahlreiche Anmerkungen, Einsprüche, Bedenken, whatever. Ich stehe bezüglich des Umgangs mit der Rolle des Formulars vor neuen Unbekannten. Dies soll auch weniger ein abgeschlossenes Tutorial von mir als ein erster Schritt in eine eventuell gemeinsame Weiterentwicklung prinzipeller Art v.a. für Programmieranfänger sein. Das Projekt an sich soll natürlich nicht weiterentwickelt werden, das kann jeder selber machen. Es geht mir um die Architektur bzw. um die Rolle des Formulars an sich.
##########
Update 18.06.: Im Projekt MVP Oberbossform hatte sich noch ein wenig an Testcode befunden, der da nicht reingehörte.
die von @Yanbel angestoßene Diskussion bzgl. MVVM@WinForms hat mich zum Schreiben dieses Threads geführt. Ich möchte explizit seinem geplanten Thread nicht vorgreifen. Da ich allerdings keine Ahnung habe, was kommt, ich aber auch nicht weiß, wann es kommt, folgt hier eine kleine WinForms-Architekturdesignentwicklung, die der ein oder andere, der vielleicht genauso fachfremd wie ich ist, als Anleitung sehen könnte, etwas besser sein eigenes Projekt zu strukturieren oder neue Möglichkeiten gezeigt zu bekommen. Dazu habe ich erstmal fünf Entwicklungsstufen eines Projekts erzeugt. Für Pros und alte Hasen dürfte dieser Thread aber wahrscheinlich wenig interessant sein.
Das eigentliche Projekt, welches in seinen 5 Stufen hier beschrieben wird, befindet sich als Gesamtpaket im Anhang und ist an sich lächerlich. Es ist ein einfaches Pseudowürfelspiel gegen den Computer und bedarf keiner Erklärung. Auch die verwendete Lade-/Speichertechnik für die Statistik ist ein Witz. Aber genau dabei soll es auch bleiben, um den Fokus auf den Umgang mit dem Formular (im Weiteren kurz das(!) Form, denn das ist auch der englische Begriff für Formular, daher WinForms) an sich zu behalten.
Stufe 1 - Einsteigerart
So würde es wohl ein Programmiereinsteiger machen.
Das Form weiß alles. Alle Variablen sind formweit bekannt. Keine Klasse außer Form. Die ganzen Abläufe sind in den EventHandlern der Buttons untergebracht.
Vorteile
- Man kann direkt alle auf dem Form anzuzeigenden Texte und andere GUI-Veränderungen vornehmen.
- Der Code ist sehr kompakt (im Vergleich zu den anderen Ausbaustufen).
- KISS - zumindest für den Anfang
Nachteile
- Verletzung von SoC, SRP, DRY und zweifellos anderen Prinzipien auch.
- Testbarkeit? So ziemlich unmöglich.
- Der Anfang vom Spaghetticodechaos, in den man sich irgendwann als Codeanfänger verfängt, nie mehr rausfindet und deshalb früher oder später frustriert aufgibt.
Stufe 2 - Klumpenform
Immer noch ist das Form der Oberboss. Es hat zumindest mal eine weitere Klasse in den Code geschafft, auch wenn es nur minimal besser als Stufe 1 ist.
Vorteil
- Zumindest gehören die Spielvariablen nicht mehr zum Form direkt, sondern in eine Klasse, die dafür auch verantwortlich ist.
Nachteile
- siehe Stufe 1
Stufe 3 - Klassennutzung
Die erste Klasse wurde stark ausgebaut, die Spielmechanik (alias Business Logic?) steckt nun in ihr. Auch das Speichern und Laden ist in eine eigene Klasse gewandert, wurde also vom Rest abgespaltet.
Ich habe auch absichtlich Aufrufe der Art EventHandler->Integration->Operation drin. Im
BtnThrowDice.Click
-EventHandler wird erst ThrowDice
aufgerufen, was wiederum Game.ThrowDice()
aufruft.Ich teile gern meine Forms in mehrere Partial files auf: EventHandler, Integration, Operation und andere
Dadurch kann ich mir schneller eine Geschichte erzählen lassen: Was kann der User u.a. machen (EventHandler), was passiert dann (Integration), und wenn ich an den Details interessiert bin, also wie dann etwas passiert, dann schau ich mir die Operation-Datei an.
Während Integration bei mir nur formeigene Funktionsaufrufe enthält, werden in Operation andere Klasseninstanzen zur Mitarbeit aufgefordert und man verlässt quasi das formeigene Gebiet.
Zur Übersicht habe ich allerdings keine eigenen partial files in den Projekten erzeugt.
Vorteile
- Plötzlich sieht man, was wo passiert und welche Klasse wofür konkret verantwortlich ist. Das Speichern und Laden soll geändert werden? Dafür ist die Klasse GameFileContentManager nun verantwortlich. Es sollen weitere Würfel hinzukommen? Man ändert die Game-Klasse.
- für CCD-Fans: IOSP ist an dieser Stelle viel einfacher umzusetzen
- Man kann mit Partial files nun viel besser trennen: geht es um EventHandler? Will man nur Prozeduren anschauen, die das GUI manipulieren? Oder ist man nur an Validierungsfunktionen interessiert? Da kann man sehr gut eigene Aufteilungen der Klassen angehen, wo vorher nur Mischmasch-in-Prozeduren war. Dies ist zwar nicht erst durch die hier eingeführte Klassennutzung möglich, aber es geht einfacher, da nun eine Monolithenklasse selber aufgeteilt wird.
Nachteile
- Der Code wird plötzlich gefühlt 3x so groß, bedingt durch gegenseitige Aufrufe und viele Prozedurrümpfe.
Stufe 4.1 – MVP Oberbossform
Keine Ahnung, ob die Stufe 4 das ist, was man als eine Art von Model-View-Presenter bezeichnet, aber es klingt ein wenig nach dem (nicht vollständig! das Model weiß bei mir nix von ViewModel und Form!), was man Martin Fowler unter der Bezeichnung Supervising Controller zuschreibt, glaubt man dem Wikipedia-Eintrag.
Das Form bleibt weiterhin Oberboss, erzeugt aber eine ViewModel-Instanz, welches sich um den Kontakt zur Spielmechanik kümmert. Das Form selber sendet Befehle ans ViewModel, welches diese dann umsetzt. Werden GUI-Updates benötigt, teilt das ViewModel dies über Property-Änderungen und dem EventRaising von PropertyChanged (als Implementierung der INotifyPropertyChanged-Schnittstelle) mit, wodurch eine BindingSource auf dem Form dies an die einzelnen GUI-Elemente weitergibt. Für jede GUI-Property-Änderung wird also quasi eine Klassenproperty im ViewModel benötigt. Will man auch z.B. Texteingaben aus einer TextBox ans ViewModel weiterleiten, muss man natürlich die Private Setters in Public Setters umwandeln.
Vorteile
- Das Form macht fast nichts mehr, nur noch das Weiterleiten der Eingaben an das ViewModel. ViewModel-Properties sind über eine BindingSource mit dem GUI verbunden.
- Das ViewModel bleibt bei einer Aufgabe: Sich um die Koordinierung des Datenmodell zu kümmern. Indem es selbst seine Daten ändert und eventuelle Änderungsevents feuert, ist es ihm egal, wer damit was anfängt. Das ViewModel weiß damit nichts über das Form.
Nachteile
- Für jede Property, die man auf dem GUI ändern will, braucht es eine eigene ViewModel-Property. Und zwar allein wegen des Event-Feuerns komplett ausformuliert – zumindest bis Microsoft sich da was einfacheres überlegt. Zum Glück kann man die in ne Partial-Class-File auslagern oder notfalls in einer Region verstecken.
Stufe 4.2 – MVP Passivform
In dieser Ausbaustufe wird das Form komplett zur Anzeige degradiert. Das ViewModel übernimmt die Änderungen am GUI. Dies wird dadurch erreicht, dass man in den Projekteigenschaften das Anwendungsframework deaktiviert, Sub Main als Startprozedur einstellt, ein Modul erstellt und dort eine Sub Main definiert, die das ViewModel als Oberklasse bestimmt, welches wiederum das Form aufruft.
Vorteile
- Das Form ist nur noch für eine Sache zuständig: GUI-Design. Keine BindingSource, keine Events.
- Das ViewModel kann das Form direkt manipulieren, ohne dafür eigene Properties hernehmen zu müssen.
Nachteile
- Man muss jedes Event des Forms, an dem man interessiert ist, per
AddHandler
abonnieren und beim Beenden am besten wieder abbestellen. Eine Verwendung derHandles
-Klausel ist nicht möglich. - Das ViewModel hat eine weitere Aufgabe bekommen (für die SRP-Puristen)
Wie geht’s weiter? Ich habe keine Ahnung, da ich Stufe 4 noch nie praktisch außerhalb des hiesigen Demoprojektes angewendet habe. Jetzt sind Eure Erfahrungen und Tipps gefragt. Zweifellos kommen zahlreiche Anmerkungen, Einsprüche, Bedenken, whatever. Ich stehe bezüglich des Umgangs mit der Rolle des Formulars vor neuen Unbekannten. Dies soll auch weniger ein abgeschlossenes Tutorial von mir als ein erster Schritt in eine eventuell gemeinsame Weiterentwicklung prinzipeller Art v.a. für Programmieranfänger sein. Das Projekt an sich soll natürlich nicht weiterentwickelt werden, das kann jeder selber machen. Es geht mir um die Architektur bzw. um die Rolle des Formulars an sich.
##########
Update 18.06.: Im Projekt MVP Oberbossform hatte sich noch ein wenig an Testcode befunden, der da nicht reingehörte.
Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.
Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „VaporiZed“ ()