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
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.
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.
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.
Immer wenn sich ein Wert in der Ansicht ändert, dh ein
TextChanged
Ereignis in .Net / C #. Dies löst eine aus,DataChangedEvent
die 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 einDataChangedEvent
. Wenn ein Benutzer Änderungen festschreiben möchte, wird ein Fehler ausgelöstSaveEvent
, der zum Speichern in das Modell übergeht. In diesem Fall ahmt das Modell die Daten der Ansicht nach und verarbeitet Aktionen.Ähnlich wie bei # b1 wird die Ansicht jedoch nicht immer mit dem Modell synchronisiert. Stattdessen
SaveEvent
wird 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)
Die Ansicht verfügt über Eigenschaftsfelder für alle Daten, die als Domänen- / Geschäftsobjekte angezeigt werden. B.
view.Animals
macht eineIEnumerable<IAnimal>
Eigenschaft verfügbar, obwohl die Ansicht diese in einem TreeView zu Knoten verarbeitet. Dann würde es für das ausgewählte TierSelectedAnimal
alsIAnimal
Eigentum aussetzen .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.
Sie erstellen eine große Ansicht, die alle einzelnen Steuerelemente enthält, die über die Ansichtsschnittstelle verfügbar gemacht werden.
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.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.
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.
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.
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.
quelle
Antworten:
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.
quelle
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).
quelle
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:
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.
quelle