Wie richte ich MVP für eine Winforms-Lösung ein?


14

Ich habe in der Vergangenheit MVP und MVC verwendet, und ich bevorzuge MVP, da es meiner Meinung nach den Ausführungsfluss so viel besser steuert.

Ich habe meine Infrastruktur (Datenspeicher- / Repository-Klassen) erstellt und verwende sie problemlos, wenn ich Beispieldaten fest codiere. Jetzt gehe ich auf die GUI und bereite mein MVP vor.

Abschnitt a

  1. Ich habe gesehen, dass MVP die Ansicht als Einstiegspunkt verwendet, das heißt, in der Konstruktormethode für Ansichten wird der Presenter erstellt, wodurch wiederum das Modell erstellt wird und Ereignisse nach Bedarf verkabelt werden.

  2. Ich habe den Presenter auch als Einstiegspunkt gesehen, an dem eine Ansicht, ein Modell und ein Presenter erstellt werden. Dieser Presenter erhält dann in seinem Konstruktor eine Ansicht und ein Model-Objekt, um die Ereignisse zu verknüpfen.

  3. Wie in 2, aber das Modell wird nicht an den Präsentator übergeben. Stattdessen ist das Modell eine statische Klasse, in der Methoden aufgerufen und Antworten direkt zurückgegeben werden.

Abschnitt b

In Bezug auf die Synchronisation von Ansicht und Modell habe ich gesehen.

  1. Immer wenn sich ein Wert in der Ansicht ändert, dh ein TextChangedEreignis in .Net / C #. Dies löst eine aus, DataChangedEventdie in das Modell übertragen wird, um es jederzeit synchron zu halten. Und wenn sich das Modell ändert, dh wenn ein Hintergrundereignis abgehört wird, wird die Ansicht auf die gleiche Weise aktualisiert, indem ein DataChangedEvent. Wenn ein Benutzer Änderungen festschreiben möchte, wird ein Fehler ausgelöst SaveEvent, der zum Speichern in das Modell übergeht. In diesem Fall ahmt das Modell die Daten der Ansicht nach und verarbeitet Aktionen.

  2. Ähnlich wie bei # b1 wird die Ansicht jedoch nicht immer mit dem Modell synchronisiert. Stattdessen SaveEventwird ausgelöst, wenn der Benutzer Änderungen festschreiben möchte, und der Präsentator erfasst die neuesten Details und übergibt sie an das Modell. In diesem Fall kennt das Modell die Ansichtsdaten erst, wenn es darauf reagieren muss. In diesem Fall werden alle erforderlichen Details übergeben.

Abschnitt C

Anzeige von Geschäftsobjekten in der Ansicht, dh ein Objekt (MyClass) nicht primitiven Daten (int, double)

  1. Die Ansicht verfügt über Eigenschaftsfelder für alle Daten, die als Domänen- / Geschäftsobjekte angezeigt werden. B. view.Animalsmacht eine IEnumerable<IAnimal>Eigenschaft verfügbar, obwohl die Ansicht diese in einem TreeView zu Knoten verarbeitet. Dann würde es für das ausgewählte Tier SelectedAnimalals IAnimalEigentum aussetzen .

  2. Die Ansicht verfügt nicht über Kenntnisse zu Domänenobjekten. Sie macht die Eigenschaften nur für die in Primitive / Framework (.Net / Java) enthaltenen Objekttypen verfügbar. In diesem Fall übergibt der Präsentator ein Adapterobjekt an das Domänenobjekt. Der Adapter übersetzt dann ein bestimmtes Geschäftsobjekt in die Steuerelemente, die in der Ansicht sichtbar sind. In diesem Fall muss der Adapter Zugriff auf die tatsächlichen Steuerelemente in der Ansicht haben, und nicht auf eine beliebige Ansicht, damit eine engere Verbindung hergestellt wird.

Abschnitt D

Mehrere Ansichten zum Erstellen eines einzelnen Steuerelements. Dh Sie haben eine komplexe Ansicht mit einem einfachen Modell wie das Speichern von Objekten verschiedener Typen. Sie könnten ein Menüsystem an der Seite haben, bei jedem Klick auf ein Element werden die entsprechenden Steuerelemente angezeigt.

  1. Sie erstellen eine große Ansicht, die alle einzelnen Steuerelemente enthält, die über die Ansichtsschnittstelle verfügbar gemacht werden.

  2. Sie haben mehrere Ansichten. Sie haben eine Ansicht für das Menü und ein leeres Bedienfeld. Diese Ansicht erstellt die anderen erforderlichen Ansichten, zeigt sie jedoch nicht an (visible = false). Diese Ansicht implementiert auch die Benutzeroberfläche für jede darin enthaltene Ansicht (z. B. untergeordnete Ansichten), sodass sie einem Präsentator angezeigt werden kann. Das leere Feld ist mit anderen Ansichten ( Controls.Add(myview)) und ( (myview.visible = true) gefüllt . Die in diesen "untergeordneten" Ansichten ausgelösten Ereignisse werden von der übergeordneten Ansicht verarbeitet, die das Ereignis wiederum an den Präsentator weiterleitet, und umgekehrt, um Ereignisse wieder an untergeordnete Elemente weiterzuleiten.

  3. Jede Ansicht, sei es die übergeordnete Hauptansicht oder eine kleinere untergeordnete Ansicht, ist mit einem eigenen Präsentator und Modell verbunden. Sie können ein Ansichtssteuerelement einfach in ein vorhandenes Formular einfügen, und die Funktionalität ist bereit. Sie müssen lediglich hinter den Kulissen eine Verbindung zu einem Präsentator herstellen.

Abschnitt E

Sollte alles über eine Schnittstelle verfügen, wirkt sich dies auf die Vorgehensweise des MVP in den obigen Beispielen aus, da diese möglicherweise nicht kompatibel sind.

  1. Alles hat eine Oberfläche, die Ansicht, Presenter und Modell. Jede davon hat dann offensichtlich eine konkrete Umsetzung. Auch wenn Sie nur eine konkrete Ansicht, ein Modell und einen Präsentator haben.

  2. Die Ansicht und das Modell haben eine Schnittstelle. Dadurch können sich die Ansichten und Modelle unterscheiden. Der Präsentator erstellt / erhält eine Ansicht und ein Modell von Objekten und dient nur dazu, Nachrichten zwischen ihnen zu übertragen.

  3. Nur die Ansicht hat eine Schnittstelle. Das Modell verfügt über statische Methoden und wird nicht erstellt. Daher ist keine Schnittstelle erforderlich. Wenn Sie ein anderes Modell wünschen, ruft der Präsentator einen anderen Satz statischer Klassenmethoden auf. Da das Modell statisch ist, hat es keine Verbindung zum Präsentator.

Persönliche Gedanken

Von all den verschiedenen Variationen, die ich vorgestellt habe (die meisten habe ich wahrscheinlich in irgendeiner Form verwendet), von denen ich sicher bin, dass es mehr gibt. Ich bevorzuge A3, da die Geschäftslogik außerhalb von MVP wiederverwendbar bleibt, B2 für weniger Datenduplizierung und weniger ausgelöste Ereignisse. C1 Wenn Sie keine andere Klasse hinzufügen, stellen Sie sicher, dass eine kleine Menge nicht testbarer Logik in eine Ansicht eingefügt wird (wie ein Domänenobjekt visualisiert wird), dies kann jedoch durch Code überprüft oder einfach in der Anwendung angezeigt werden. Wenn die Logik komplex wäre, würde ich einer Adapterklasse zustimmen, aber nicht in allen Fällen. Für Abschnitt D denke ich, dass D1 eine Ansicht erstellt, die zumindest für ein Menübeispiel zu groß ist. Ich habe vorher D2 und D3 benutzt. Das Problem mit D2 ist, dass Sie am Ende viel Code schreiben müssen, um Ereignisse vom und zum Präsentator in die richtige untergeordnete Ansicht zu leiten. Jede neue Steuerung benötigt mehr Kabel, um den einzelnen Presenter zu unterstützen. D3 ist meine bevorzugte Wahl, fügt aber noch weitere Klassen als Präsentatoren und Modelle hinzu, um mit der Ansicht umzugehen, selbst wenn die Ansicht sehr einfach ist oder nicht wiederverwendet werden muss. Ich denke, eine Mischung aus D2 und D3 ist am besten auf die Umstände. In Bezug auf Abschnitt E denke ich, dass alles, was eine Schnittstelle hat, überflüssig sein könnte. Ich mache dies bereits für Domänen- / Geschäftsobjekte und sehe oft keinen Vorteil im "Design", aber es hilft beim Verspotten von Objekten in Tests. Persönlich würde ich E2 als klassische Lösung sehen, obwohl ich E3 in 2 Projekten gesehen habe, an denen ich zuvor gearbeitet habe. Ich denke, eine Mischung aus D2 und D3 ist am besten auf die Umstände. In Bezug auf Abschnitt E denke ich, dass alles, was eine Schnittstelle hat, überflüssig sein könnte. Ich mache dies bereits für Domänen- / Geschäftsobjekte und sehe oft keinen Vorteil im "Design", aber es hilft beim Verspotten von Objekten in Tests. Persönlich würde ich E2 als klassische Lösung sehen, obwohl ich E3 in 2 Projekten gesehen habe, an denen ich zuvor gearbeitet habe. Ich denke, eine Mischung aus D2 und D3 ist am besten auf die Umstände. In Bezug auf Abschnitt E denke ich, dass alles, was eine Schnittstelle hat, überflüssig sein könnte. Ich mache dies bereits für Domänen- / Geschäftsobjekte und sehe oft keinen Vorteil im "Design", aber es hilft beim Verspotten von Objekten in Tests. Persönlich würde ich E2 als klassische Lösung sehen, obwohl ich E3 in 2 Projekten gesehen habe, an denen ich zuvor gearbeitet habe.

Frage

Implementiere ich MVP richtig? Gibt es eine richtige Vorgehensweise?

Ich habe Martin Fowlers Arbeit gelesen, die Variationen aufweist, und ich erinnere mich, dass ich als ich anfing, MVC zu machen, das Konzept verstand, aber ursprünglich nicht herausfinden konnte, wo der Einstiegspunkt ist, alles hat seine eigene Funktion, aber was steuert und schafft das Original Satz von MVC-Objekten.


2
Der Grund für diese Frage ist, dass ich mich bemühe, sie beim ersten Versuch richtig zu stellen. Ich möchte lieber ein Standard-MVP verwenden, als 6 Anwendungen mit verschiedenen Variationen für dasselbe Muster zu erstellen.
JonWillis

Perfekt ... Ich wollte es schon lange fragen
Der König

Antworten:


4

Vieles, was Sie hier präsentieren, ist sehr vernünftig und solide. Einige der Auswahlmöglichkeiten hängen von den Besonderheiten der Anwendung ab und davon, welche sich richtig "anfühlt". Wie in den meisten Fällen wird es keine richtige Antwort geben. Einige der Entscheidungen sind hier sinnvoll, und diese Entscheidungen können für die nächste Anwendung und die nächsten Umstände völlig falsch sein. Ich glaube, Sie sind auf dem richtigen Weg, ohne die Details der App zu kennen, und haben einige fundierte, nachdenkliche Entscheidungen getroffen.

Für mich ist der Presenter fast immer der Einstiegspunkt. Wenn Sie die Benutzeroberfläche als Einstiegspunkt verwenden, wird die Benutzeroberfläche zu logisch und Sie können keine neue Benutzeroberfläche mehr ersetzen, ohne dass sich große Änderungen an der Codierung ergeben. Und das ist wirklich die Aufgabe des Präsentators.


Vielleicht ist die Frage, die ich stellen sollte, eine der Möglichkeiten, MVP zu verwenden, einfach falsch. Ich würde Variationen für verschiedene Szenarien zustimmen, bin jedoch der Meinung, dass ein allgemein akzeptierter Ansatz erforderlich ist. Die Beispiele für MVP sind alle einfache Beispiele und fast alle mit demselben Bedarf, ein Domänenobjekt zu bearbeiten und Änderungen zu speichern. Aus diesen Beispielen mit dem gleichen Ziel wurden jedoch die obigen Variationen hergestellt. Ich habe gerade einen Teil einer App mithilfe von Ereignissen codiert, die in der Ansicht ausgelöst wurden, um im Präsentator verarbeitet zu werden, aber ich hätte die Ansicht einen Verweis auf den Präsentator enthalten und Methoden direkt aufrufen lassen können.
JonWillis

Ich würde allen Bemühungen zustimmen, die Ansicht zu vereinfachen, um zu rechtfertigen, dass es sich nicht um ein Unit-Test handelt. Dann sollte der Präsentator die Kontrolle haben. Meine einzige Sorge dabei (ich denke, das könnte falsch sein) ist, dass beispielsweise WinForms / Benutzersteuerelemente mit übergeordneten Elementen verknüpft werden müssen, und ich mich frage, ob ein Moderator zusätzliche Logik benötigt, um sie zu schreiben.
JonWillis

@ Jon - Wege MVP sind falsch? In meinem Buch wäre es, wenn die Ansicht über den Moderator weiß. Es ist vielleicht nicht "falsch" in dem Sinne, dass es funktioniert, aber es wäre einfach nicht MVP, es verwandelt sich an diesem Punkt in etwas anderes. Das Problem bei Entwurfsmustern ist, dass die Beispiele immer so klar und einfach wie möglich sind. Dann, wenn Sie sie zum ersten Mal implementieren und die reale Welt aufspringt und sagt: "Hey, echte Apps sind viel komplizierter als dieses mickrige Beispiel." Hier beginnt Ihr Wachstum. Finden Sie heraus, was für Sie und die Umstände der App geeignet ist.
Walter

Danke für den Hinweis. Ich erinnere mich, MVC an der Universität gelernt zu haben. Es klang großartig, aber mit einer leeren Leinwand fragt man sich, wo man anfangen soll und wie es wirklich funktioniert. In Bezug auf MVP, das falsch ist, meine ich, waren irgendwelche der Ideen / Variationen von MVC, die ich oben gepostet habe, die falsche Vorgehensweise, dh wenn die Ansicht weiß, wie der Präsentator funktioniert. Oder sollte die Ansicht einen Verweis auf eine Oberfläche eines Präsentators oder eines konkreten Typs usw. enthalten? Nur viele verschiedene Variationen, die alle für den gleichen Zweck funktionieren können.
JonWillis

1
@ Jon - Ihre Variationen stimmen weitgehend mit dem MVP-Gedanken überein. Was die Arbeit mit Interfaces oder konkreten Typen angeht, hängt dies von den Umständen der App ab. Wenn die App recht klein und nicht sehr komplex ist, ist das Hinzufügen von Schnittstellen möglicherweise nicht erforderlich. Ich halte die Dinge so einfach wie möglich, bis klar ist, dass die App unbedingt die X-Architektur implementieren muss. Weitere Informationen finden Sie in dieser Antwort: programmers.stackexchange.com/questions/34547/…
Walter

4

Wir verwenden eine modifizierte Form von MVP in unserer .NET 2.0 Winforms-App. Die beiden fehlenden Teile waren ein modifizierter Adapter des WPF ViewModel und das Hinzufügen von Datenbindungen. Unser spezielles Muster ist MVPVM.

Wir verkabeln in fast allen Fällen als Presenter-First, mit Ausnahme von benutzerdefinierten Benutzersteuerelementen, die aus Gründen der Designerfreundlichkeit als View-First verkabelt werden. Wir verwenden Dependency Injection, code-generierte ViewModels, BDD für die Presenter und TDD / TED für das Modell.

Die VMs sind nur ein massives, flaches Bündel von Eigenschaften, die PropertyChanged auslösen, wenn sie geändert werden. Es war sehr einfach, diese durch Code (und die damit verbundenen Testeinheiten) zu generieren. Wir verwenden sie zum Lesen und Schreiben in benutzerinteragierbaren Steuerelementen und zum Steuern der aktivierten Status. Das ViewModel ist mit dem View gekoppelt, da wir die Datenbindung so gut wie für alles andere verwenden.

In der Ansicht sind gelegentlich Methoden verfügbar, mit denen die VM bestimmte Aufgaben nicht ausführen kann. Dies kontrolliert normalerweise die Sichtbarkeit von Objekten (WinForms kann sehr wählerisch sein) und Dingen, die sich weigern, datengebunden zu sein. In der Ansicht werden immer Ereignisse wie "Anmelden" oder "Neustart" mit den entsprechenden EventArgs angezeigt, um auf Benutzerverhalten zu reagieren. Sofern wir keinen Hack wie "View.ShowLoginBox" verwenden mussten, ist die Ansicht vollständig austauschbar, solange sie die allgemeinen Designanforderungen erfüllt.

Wir haben ungefähr 6-8 Monate gebraucht, um dieses Muster festzuhalten. Es hat viele Teile, ist aber sehr flexibel und extrem leistungsstark. Unsere spezifische Implementierung ist sehr asynchron und ereignisgesteuert, was möglicherweise eher ein Artefakt anderer Anforderungen als ein Nebeneffekt des Entwurfsverhaltens ist. Ich habe zum Beispiel die Thread-Synchronisation zu der Basisklasse hinzugefügt, von der unsere VMs geerbt haben (wodurch einfach eine OnPropertyChanged-Methode zum Auslösen des Ereignisses verfügbar gemacht wurde).


Ich weiß, dass Ihre MVPVM von kommerziellem Interesse sein kann, aber wenn es in Ordnung ist, können Sie einen Beispielcode bereitstellen? Wo ziehen Sie die Grenze zwischen der Funktionsweise des Modells und der Funktionsweise des Präsentators? Ich habe gesehen, dass Presenter so einfach sind, dass sie Ereignisse anzeigen und nur das Modell aufrufen. Der Presenter greift auf die Datenebenen zu, um Geschäftsobjekte an ein Modell zu übergeben, bis das Modell vom Presenter ersetzt wird.
JonWillis

Ja, lassen Sie mich ein Beispiel beenden. Es wird leicht auf unser Geschäft zugeschnitten sein, da ich es als firmeninternes Schulungsinstrument verwende.
Bryan Boettcher

Vielen Dank, ich werde am Wochenende nachsehen, ob ich es verstehe
JonWillis

@insta - Der Link ist nicht verfügbar. Könnten Sie ihn irgendwo erneut hochladen?
Yves Schelpe

1
Mist, ich habe es gerade wie vor einer Woche gelöscht. Abbildungen :(
Bryan Boettcher

2

Ich verwende eine Version von PureMvc, die für .NET geändert und dann von mir selbst erweitert wurde.

Ich war es gewohnt, PureMvc in Flex-Anwendungen zu verwenden. Es handelt sich hierbei um ein Framework, das sich leicht anpassen lässt, wenn Sie es anpassen möchten.

Ich habe mir folgende Freiheiten damit genommen:

  • Durch Reflektion konnte ich private Eigenschaften in den View-Mediator holen, um in die Formularklasse zu gelangen und den (privaten) Verweis auf jedes von mir vermittelte Steuerelement abzurufen.
  • Mit Reflection kann ich die Steuerelemente automatisch mit den generischen Ereignissignaturen in meinem Mediator verknüpfen, sodass ich nur den Absenderparameter einschalte.
  • Normalerweise möchte PureMvc, dass abgeleitete Mediatoren ihre Benachrichtigungsinteressen in einer Funktion definieren, die ein Array von Zeichenfolgen zurückgibt. Da meine Interessen größtenteils statisch sind und ich die Interessen der Mediatoren leichter erkennen möchte, habe ich eine Änderung vorgenommen, damit der Mediator seine Interessen auch durch eine Reihe von Mitgliedsvariablen mit einer bestimmten Signatur angeben kann: _ _ _ <Klassenname> _ <Benachrichtigungsname>. Auf diese Weise kann ich mithilfe des IDE-Member-Baums sehen, was vor sich geht, anstatt in eine Funktion zu schauen.

In PureMvc können Sie einen Befehl als Einstiegspunkt verwenden. Mit einem typischen Startbefehl wird das Modell so weit wie möglich eingerichtet. Anschließend wird das Hauptformular erstellt, der Mediator für dieses Formular erstellt und registriert. Anschließend wird Application.Run ausgeführt auf dem Formular.

Der Vermittler für das Formular ist für die Einrichtung aller Untervermittler verantwortlich. Ein Teil davon kann wiederum mithilfe von Reflexionstricks automatisiert werden.

Das System, das ich verwende, ist Drag & Drop-kompatibel, wenn ich Ihre Bedeutung verstehe. Das eigentliche Formular wird alle in VS erstellt, aber meine Erfahrung ist nur mit Formularen, die statisch erstellte Steuerelemente haben. Dinge wie dynamisch erstellte Menüelemente scheinen mit ein wenig Feineinstellung des Vermittlers für dieses Menü oder Untermenü machbar zu sein. Haarig würde es werden, wenn der Mediator kein statisches Wurzelelement hatte, an das er gebunden werden konnte, und Sie dynamische 'Instanz'-Mediatoren erstellten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.