Share via


MVPVM-Entwurfsmuster

Das Model-View-Presenter-ViewModel-Entwurfsmuster für WPF

Bill Kratochvil

Beispielcode herunterladen.

Von all den erfolgreichen Projekten, an denen ich mitgearbeitet habe, war den erfolgreichsten ein Ergebnis gemeinsam: Mit zunehmenden Umfang der Anwendung wurde die Codebasis kleiner. Oberflächlich betrachtet mag dies als Widerspruch erscheinen. Die Kodierung in einer Agile-Umgebung macht jedoch die Reduzierung der Codebasis möglich. Mit den geänderten Anforderungen einher gehen Umgestaltungen. Diese Umgestaltungen ermöglichen in Verbindung mit den Erfahrungen mit dem vorhandenen Code die effizientere Wiederverwendung vorhandener Komponenten und gleichzeitig die Beseitigung von dupliziertem Code während des Prozesses.

Im Gegensatz hierzu stehen monolithische Projekte, die manchmal als erfolgreich gelten, in denen der Quellcode immer weiter wächst und es wenig Möglichkeiten für eine Wiederverwendung gibt. Diese Projekte beanspruchen Ressourcen auf intensive Weise und stellen ein Risiko für das zukünftige Wachstum dar. Was ist der grundlegende Unterschied? Der Unterschied ist die Infrastruktur des verwendeten Entwurfsmusters. Die von Ihnen verwendeten Muster entscheiden, ob Sie sich in die Ecke manövrieren oder den Chancen, die die Zukunft möglicherweise bietet, den Rücken zukehren.

In diesem Artikel stelle ich Ihnen eines dieser Muster vor, das von zahlreichen Windows Presentation Foundation (WPF)-Entwicklern infolge der Verbreitung des Model-View-ViewModel (MVVM)-Musters gerne übersehen wird. Dieses Muster ist das Model-View-Presenter-ViewModel (MVPVM)-Muster. Dieses Entwurfsmuster für Unternehmensanwendungen wurde im Prism-Projekt von Microsoft für Muster und Verfahren eingeführt (Prism.CodePlex.com). (Hinweis: Dieses Muster erhielt keinen Namen, daher bezeichne ich es als MVPVM.) Das Prism-Projekt wird am besten mit einem Auszug aus der Übersicht auf der Website beschrieben:

"Prism stellt Anleitungen bereit, die Ihnen beim einfacheren Entwerfen und Erstellen funktionsreicher, flexibler und einfach zu wartender Windows Presentation Foundation (WPF)-Desktopanwendungen, Silverlight Rich Internet Applications (RIAs) und Windows Phone 7-Anwendungen helfen sollen. Mittels Entwurfsmustern, die wichtige Prinzipien für das Architekturdesign anwenden, wie die Trennung von Aufgaben und die lose Verbindung, hilft Prism Ihnen beim Entwerfen und Erstellen von Anwendungen mittels lose verbundener Komponenten, die unabhängig voneinander weiterentwickelt werden können, gleichzeitig jedoch einfach und nahtlos in die Gesamtanwendung integriert werden können. Diese Arten von Anwendungen werden zusammengesetzte Anwendungen genannt."

Die Verbreitung und der Erfolg von MVVM haben MVPVM überschattet, auch wenn MVPVM die Weiterentwicklung von MVVM darstellt (was in diesem Artikel gezeigt werden wird). MVPVM stellt die gesamte Leistungsfähigkeit und Funktionalität von MVVM bereit und führt gleichzeitig die Skalierbarkeit und Erweiterbarkeit des Model-View-Presenter (MVP)-Musters ein. Wenn Sie der Ansicht sind, dass MVVM eine Weiterentwicklung von MVP für WPF darstellt, werden Sie diesen Artikel interessant finden.

Um die Leistungsfähigkeit und die Vorteile von MVPVM vollständig verstehen zu können, müssen wir die Entwicklung dieses Musters kennen – wir müssen unsere Geschichte verstehen. Das Model-View-Controller (MVC)-Muster ist eine unabdingbare Komponente, um dieses Verständnis erzielen zu können. Daher stelle ich Ihnen zunächst MVC vor, und das auf eine Weise, die für Sie völlig neu ist.

Das MVC-Muster

Bitte beachten Sie, dass sich diese Diskussion von MVC auf Desktopanwendungen bezieht. Webanwendungen sind ein anderer Fall und nicht Gegenstand dieses Artikels.

Martin Fowler sagt in seinem Artikel "GUI Architectures" (GUI-Architekturen, bit.ly/11OH7Y) Folgendes über MVC: "Verschiedene Leser, die über MVC in verschiedenen Artikeln lesen, erhalten verschiedene Eindrücke und beschreiben diese als "MVC". Wenn dies noch nicht genügend Verwirrung stiftet, dann gibt es noch genügend weitere Missverständnisse hinsichtlich MVC, die durch die stille Post entstehen." Der Artikel "Whatsa Controller Anyway" (Was ist überhaupt ein Controller) unter bit.ly/7ajVeS fasst dies passend zusammen: "Informatiker tendieren im Allgemeinen dazu, Begriffe mit Bedeutungen zu überladen. Sie weisen dem gleichen Wort gerne mehrere Bedeutungen zu (die sich manchmal auch noch widersprechen)." 

Das Verständnis von Smalltalk MVC wird weiter dadurch erschwert, dass die Entwickler nicht über die gleichen Kenntnisse über die Architekturumgebung wie die Architekten verfügen. Die Überladung von Begriffen und das Stille-Post-Verfahren werden dadurch noch komplizierter, dass viele von uns noch nicht einmal wissen, was ein Controller ist, da wir noch nie einen Controller verwenden mussten. Das Betriebssystem hat das immer für uns erledigt. Mithilfe einiger weniger Fakten, um die mangelnden Kenntnisse auszugleichen, wird MVC tatsächlich leicht verständlich. Das "C" in MVC hat nichts mit dem "P" in MVP gemeinsam.

Der Controller hat einen konkreten Ursprung, den zu kennen es sich lohnt. (Dieser hat aber nicht notwendigerweise etwas mit den modernen MVC-Ansätzen im Zusammenhang mit aktuellen Technologien gemeinsam.) Der Vater von MVC, Trygve Reenskaug, schrieb darüber 1979 in seinem Artikel über Models-Views-Controllers (bit.ly/2cdqiu). Dieser Artikel stellt einige Einblicke in den Zweck des Controllers bereit. Trygve Reenskaug schrieb damals:

"Der Controller ist die Verbindung zwischen dem Benutzer und dem System. Er stellt dem Benutzer Daten bereit, indem er relevante Ansichten an geeigneten Stellen auf dem Bildschirm anordnet. Er stellt dem Benutzer Ausgabemöglichkeiten bereit, indem er dem Benutzer Menüs oder andere Wege bereitstellt, Befehle und Daten einzugeben. Der Controller empfängt die Benutzerausgaben, übersetzt diese in die entsprechenden Nachrichten und übergibt diese Nachrichten an eine oder mehrere Ansichten.

Der Controller sollte die Ansichten niemals ergänzen. Er sollte beispielsweise niemals die Ansichten von Knoten miteinander verbinden, indem er Pfeile zwischen diesen zeichnet.

Eine Ansicht sollte im Gegenzug niemals etwas über Benutzereingaben wissen, wie Mausvorgänge und Tastaturanschläge. Es sollte stets möglich sein, eine Methode in einem Controller zu schreiben, die Nachrichten an Anzeigen sendet, die eine beliebige Folge von Benutzerbefehlen reproduzieren."

Dieses Konzept wird in Abbildung 1 dargestellt.

Eine "Nachricht" ist im Kontext der objektorientierten MVC-Programmierung (Object-Oriented Programming, OOP) ein Mittel zum Ausführen von Methoden. Sie wurden damals als "Nachrichten" beschrieben, da virtuelle Aufrufe ein neues Konzept darstellten und dies eine Möglichkeit war, sie von statischen Funktionsaufrufen zu unterscheiden. In Kapitel 1.2 von "Smalltalk, an Introduction to Application Development Using VisualWorks” (Smalltalk, eine Einführung in die Anwendungsentwicklung mittels VisualWorks, Prentice Hall, 1995) von Trevor Hopkins und Bernard Horan stellen die Autoren Folgendes fest: "... wenn das empfangende Objekt die Nachricht versteht, die diesem gesendet wurde, wird einer der internen Vorgänge (bzw. eine der internen Methoden) durchgeführt. Dies führt möglicherweise dazu, dass einige Berechnungen stattfinden (indem auf eine oder mehrere der internen Variablen des Objekts reagiert wird)." (Hinweis: Dieses "Nachrichten sendende" OOP-Konzept ist in Prism über dessen EventAggregator verfügbar.)

Das Buch beschreibt die Aufgaben von Smalltalk MVC in Kapitel 29, “Introduction to Models, Views and Controllers" (Einführung in Modelle, Ansichten und Controller). Dort wird gesagt, dass Controller von der Klasse Controller abgeleitet werden. Es wird Folgendes gesagt: "Instanzen dieser Klasse verweisen auf einen Sensor, der die Maus und die Tastatur darstellt, sodass sie Eingaben verarbeiten kann." Weiter wird ausgeführt, dass die beiden Aktionen, die von einem Controller initiiert werden, die Kommunikation mit dem Modell (siehe Abbildung 1) und die Kommunikation mit der Ansicht ohne Beteiligung des Modells sind.

Controller Receives Input and Sends Message (Method to Execute); Views Can’t Catch User Input
Abbildung 1 Controller empfängt Eingabe und sendet eine Nachricht (Methode zum Ausführen); Ansichten erhalten keine Informationen zu Benutzereingaben

In Smalltalk MVC hat "jede" Ansicht einen Controller, und es kann nur jeweils ein Controller aktiv sein. Im ursprünglichen Smalltalk MVC wurde die Oberfläche abgerufen. Der in der Hierarchie am weitesten oben stehende ControlManager fragt die einzelnen Controller der aktiven Ansicht ab, ob sie die Steuerung übernehmen möchten. Nur die Ansicht, in der sich der Mauszeiger befindet, kann die Steuerung übernehmen. Wenn eine Ansicht schreibgeschützt ist, ist die Klasse NoController verfügbar, die entworfen wurde, um die Steuerung abzulehnen. Eine Ansicht mit untergeordneten Ansichten muss die Controller der untergeordneten Ansichten abrufen. Wenn ein Controller die Steuerung übernommen hat, fragt der die Ergebnisse der Tastatur oder der Maus ab und sendet Nachrichten an die Ansicht oder das Modell, je nachdem, wie z. B. Mausbewegung, Maustastenklick usw. Im Fall von MVVM wäre dies der Abonnierung der Ereignisse eines Steuerelements im Codebehind der Ansicht oder über einen ViewModel-Befehl vergleichbar. Wenn ein Benutzer mit dem Steuerelement interagiert, wird die entsprechende Methode aufgerufen.

An diesem Punkt erhalten Sie einen Einblick in den Sinn und Zweck von Controllern im Kontext von MVC und einer Umgebung, die Ereignisse nicht automatisch für Sie behandelt. Anders als Smalltalk MVC-Entwickler müssen WPF-Entwickler keine Tastaturpuffer abfragen und Ereignisse verpacken und auslösen. Sie abonnieren diese lediglich. Die entsprechende Methode empfängt diese "automagisch" über das Microsoft-Ereignismuster. Mit diesem Wissen stellt sich das Folgende möglicherweise in einem anderen Licht und weniger verwirrend dar.

In seinem Artikel "Applications Programming in Smalltalk-80: How to Use Model-View-Controller (MVC)" (Anwendungsprogrammierung in Smalltalk-80: Verwendung von Model-View-Controller (MVC), bit.ly/3ZfFCX) stellt Steve Burbeck Folgendes fest:

"Der Controller interpretiert die Maus- und Tastatureingaben des Benutzers und erteilt dem Modell und/oder der Ansicht Änderungsbefehle, je nachdem. Schließlich verwaltet das Modell die Verhaltensweisen und Daten der Anwendungsdomäne, antwortet auf Anforderungen nach Informationen zum Status (in der Regel von der Ansicht ausgehend) und reagiert auf Anweisungen, den Status zu ändern (in der Regel vom Controller ausgehend)."

In Abbildung 2 wird dieses Konzept gezeigt.

Smalltalk Model-View-Controller
Abbildung 2 Smalltalk Model-View-Controller

Ein letzter Punkt, der Klarheit für ein ansonsten möglicherweise unverständliches Thema schafft, befindet sich im Artikel "Twisting the Triad, the Evolution of the Dolphin Smalltalk MVP Application Framework" (Änderungen für die Dreiheit – Die Entwicklung des Dolphin Smalltalk MVP Application Framework) von Andy Bower und Blair McGlashan unter bit.ly/o8tDTQ (PDF zum Download). Dies ist einer der umfassenderen Artikel, den ich zum Thema gelesen habe. Die Verfasser merken Folgendes in Bezug auf den Controller an: "Die Ansichten sind natürlich für die Anzeige der Domänendaten verantwortlich, während die Controller die ursprünglichen Benutzergesten behandeln, die letzten Endes zu Aktionen in Bezug auf diese Daten führen."  

Ich erwähne diesen letzten Punkt, um eine weitere Definition bereitstellen zu können. Um diesen (und andere Artikel) über MVC vollständig verstehen zu können, müssen Sie verstehen, was mit dem Begriff "Gesten" gemeint ist (siehe Abbildung 1). Während meiner Forschungen habe ich den Artikel "An Introduction to GUI Programming with UIL and Motif” (Eine Einführung in die GUI-Programmierung mit UIL und Motif, bit.ly/pkzmgc)" gelesen, in dem Folgendes gesagt wird:

"Das Wesentliche hierbei ist, dass ein Motif-Programm keine Aktionen durchführt, solange der Benutzer keine Aktionen anfordert. Die Aktionen werden durch die Auswahl einer Menüoption, das Klicken auf eine Schaltfläche bzw. ein anderes Fenster oder das Drücken einer Taste angefordert. Dies sind zusammen genommen Benutzergesten. Jede Geste löst das Ausführen bestimmter Aufgaben durch eine Anwendungsfunktion aus."

Andy Bower, Mitverfasser von "Twisting the Triad" teilte mir seine Gedanken über "Gesten" mit:

"Ich verstehe 'Gesten' als Umwandlung eines oder mehrere Benutzerereignisse in etwas Bedeutungsvolleres.

Ein TextController kann beispielsweise die Ereignisse "Pfeil nach oben" und "Pfeil nach unten" empfangen und diese in einzelne 'Tastendruckereignisse' umwandeln. Ähnlich kann ein SelectionInListController (der mit einem Listenfeld verbundene Controller) mehrere Maus-nach-unten-, Maus-Nachverfolgungs- und Maus-nach-oben-Ereignisse empfangen und diese als einzelne 'Listenauswahlgeste' behandeln.

So betrachtet, erkennen wir, dass ein modernes, ereignisgesteuertes Betriebssystem den größten Teil der Gestenverarbeitung für uns erledigt."

Zusammenfassend lässt sich zur Controllerlogik sagen, dass die Controllerfunktion in den erwähnten MVC-Varianten ziemlich konsistent ist. Da Microsoft-Steuerelemente (Widgets) die Maus- und Tastatureingaben des Benutzers behandeln, abonnieren Sie einfach die Ereignisse und weisen auf die Methode, die ausgeführt werden muss – der "Controller" in den intelligenten Steuerelementen führt die Methode für Sie auf Betriebssystemebene aus. Daher benötigen Sie keinen Controller, wie auch klar im Artikel "Twisting the Triad" ausgeführt wird. Ohne den Controller bleibt Ihnen das Model-View-Muster für Windows-Anwendungen. Sie müssen dies berücksichtigen, wenn Sie MVC und dessen Anwendungs- und Darstellungsmodelle untersuchen, da es ohne den Controller keine Dreiheit gibt (siehe Abbildung 3).

Without the Controller, It Has Been Model-View (not Model-View-Controller)
Abbildung 3 Ohne den Controller handelt es sich um Model-View (nicht Model-View-Controller)

Es sollte erwähnt werden, dass nicht nur der Controller zu den begrifflichen Verwirrungen im Zusammenhang mit MVC geführt hat. Im Fall von Smalltalk MVC ist die Geschäfts- bzw. Domänenlogik das Modell. Im Fall von VisualWorks MVC enthält das Anwendungsmodell die "Anwendungslogik", die für die Darstellung eines oder mehrerer Geschäfts- bzw. Domänenobjekte für den Benutzer erforderlich ist (siehe Abbildung 4). Dies wird im Artikel "Twisting the Triad" ausführlicher behandelt. Wenn Sie daran denken, dass das Anwendungsmodell und das Präsentationsmodell die gleiche Funktionalität haben, und der wesentliche Unterschied darin besteht, dass das Präsentationsmodell "nicht" auf die Ansicht zugreifen kann (Martin Fowler stellte den Unterschied klar, um die Begriffe nicht zu überladen), dann können WPF-Entwickler das Präsentationsmodell schnell verstehen, da es sich im Wesentlichen um MVVM handelt. John Gossman, der Vater von MVVM, erklärt dies in seinem Blogbeitrag "PresentationModel and WPF" (Präsentationsmodell und WPF) unter bit.ly/pFs6cV. Wie Martin Fowler war auch er darauf bedacht, die Begriffe nicht zu überladen. So erhalten wir ein klares Verständnis davon, was MVVM ist: Es ist eine "WPF-spezifische Version des Präsentationsmodellmusters".

VisualWorks Model-View-Controller
Abbildung 4 VisualWorks Model-View-Controller

Wenn Sie MVC einmal richtig verstanden haben, können Sie die wahren Rollen des Modells und der Ansicht verstehen. Dies sind im Wesentlichen die beiden einzigen Speicherorte für die Geschäfts- bzw. Domänenlogik. Wenn Sie "Twisting the Triad" gelesen haben, wissen Sie, dass die Aufgaben des Anwendungsmodells zum Presenter verschoben wurden und Sie den Controller nicht einfach durch einen Presenter ersetzen können. Dies ist nicht sinnvoll, da sie keine Synonyme darstellen.

Das MVP-Muster

Die Behandlung von MVP mit der erforderlichen Detailliertheit ist nicht Gegenstand dieses Artikels. Es gibt zahlreiche Artikel, die MVP behandeln, wie "Twisting the Triad" und das "Potel Paper", das Sie von bit.ly/dF4gNn (PDF) herunterladen können.

Ich weise jedoch darauf hin, dass in "Twisting the Triad" eine wichtige Feststellung gemacht wird, auf die ich angespielt habe. Diese Tatsache führte zur Entwicklung von MVC zu MVP:

"Eine weitere irreführende Eigenschaft von MVC, zumindest in Bezug auf Dolphin, bestand in der Idee, dass Controller nicht gut in die Windows-Umgebung passen. Microsoft Windows, wie die meisten modernen grafischen Betriebssysteme, stellt eine Reihe systemeigener Widgets bereit, mit denen Benutzeroberflächen erstellt werden können. Diese 'Fenster' enthalten bereits die meisten Controllerfunktionen als Teil der zugrunde liegenden Betriebssystemsteuerung."

Ich möchte außerdem die folgende Aussage von Martin Fowler in seinem Artikel zu GUI-Architekturen hervorheben: "Diese Notwendigkeit, die Widgets direkt zu bearbeiten, wurde von vielen Entwicklern als eine Art Problemumgebung betrachtet und half, den Model-View-Presenter-Ansatz zu entwickeln." Es ist wichtig, dies zu verstehen, da MVVM die Präsentationsmodell-Mentalität in das Gedächtnis zahlreicher WPF-Entwickler eingebrannt hat, die der Meinung sind, es ist schlechte Programmierarbeit, die Ansicht direkt zu aktualisieren. Dies gilt für MVVM und das Präsentationsmodell, da Sie nach dem Verweis auf die Ansicht das ViewModel nicht mehr mit einer anderen Ansicht wiederverwenden können (dies ist der Hauptgrund für diese Regel). Dieser beschränkende Faktor war einer der Gründe, warum Smalltalk MVC zum MVP-Muster entwickelt wurde. Mittels MVPVM können Entwickler auf die Ansicht und das Ansichtsmodell zugreifen (dies ist eng miteinander verbunden). Dadurch können die Ansicht und das Ansichtsmodell vollständig unverbunden bleiben (siehe Abbildung 5). Ansichten können verschiedene Ansichtsmodelle wiederverwenden, und Ansichtsmodelle können von verschiedenen Ansichten wiederverwendet werden (wie Sie später sehen werden, wenn ich das MVPVM-Muster bespreche). Dies ist einer der zahlreichen großen Vorteile von MVPVM.

Model-View-Presenter, Supervising Controller
Abbildung 5 Model-View-Presenter, Übergeordneter Controller

Andy Bower teilte mir die folgenden Informationen zum Thema mit, um weiter zur Klärung beizutragen:

"Es sollte vielleicht darauf hingewiesen werden, dass das Ziel all dieser Muster die Wiederverwendung mittels der Plugfunktion ist. Die Wahrung der losen Verbindung, wann immer möglich, und der geringen Größe der Schnittstellen ermöglicht eine maximale Wiederverwendung in Bezug auf die Art und Weise, wie die Komponenten neu kombiniert werden können.

Beachten Sie jedoch, dass Modell effektiv über zwei Schnittstellen verfügt, die mit jeder verknüpften Ansicht und jedem verknüpften Presenter kompatibel sein müssen. Bei der ersten handelt es sich um die Standardfunktion der Aufrufschnittstelle, die für das Abrufen/Festlegen von Werten und das Ausführen von Befehlen verwendet wird. Bei der zweiten handelt es sich um die Ereignisschnittstelle, bei der es sich um dieselbe Schnittstelle handeln muss, die von der Ansicht (Beobachtermuster) erwartet wird. Diese Kombination von Schnittstellen kann als ein 'Protokoll' bezeichnet werden.

In Dolphin MVP benötigt ein Listenwidget (Dreiheit) beispielsweise drei Komponenten mit kompatiblen Protokollen. ListModel, ListView und ListPresenter."

Nachdem Sie einmal verstanden haben, warum Sie eine Ansicht nicht direkt aktualisieren sollten, und erkannt haben, dass dies zu einem neuen Entwurfsmuster beigetragen hat, das Ihnen gestattet, Objekte effektiv wiederzuverwenden, indem die Beschränkung der Nicht-Aktualisierbarkeit der Ansicht beseitigt wird, dann können Sie die neuen Möglichkeiten nutzen, die Ihnen diese Entwicklung bietet. Sie leben in einer agilen Welt und müssen – wie Ihr Code – Ihre Denkmuster ändern, damit sich die Geschichte nicht wiederholt.

Das MVPVM-Muster

Mir wurde dieses Muster zu einem frühen Zeitpunkt im Lebenszyklus von Prism beigebracht, speziell in Drop 7 von CompositeWPF. Verloren in all den mystischen Mächten innerhalb von Prism, erkannte ich das MVP-Muster und konnte mich so besser orientieren. Ich lernte schnell (wie ich es auch neuen Entwicklern beibringe), einfach mit dem Presenter zu beginnen. Von hier können Sie den Code und die Anwendung analysieren. Wie die Codeausführung hierher gelangt, kann zu einem späteren Zeitpunkt erlernt werden und muss keine kritischen Termine gefährden. Die Auswirkungen der schwierigen Lernkurven können bewältigt werden.

Die folgenden Definitionen der MVPVM-Komponenten enthalten Auszüge von "Twisting the Triad", wenn zutreffend. Ich fand diese Informationen informativ, umfassend und genau. Da ich Ihnen die besten Informationen bereitstellen möchte, die verfügbar sind, zitiere ich hier die Definitionen in diesem Artikel. Diese Definitionen gelten heute genauso für MVPVM (siehe Abbildung 6) wie sie im März 2000 für MVP gegolten haben, als "Twisting the Triad" verfasst wurde.

Model-View-Presenter-ViewModel
Abbildung 6 Model-View-Presenter-ViewModel

(Hinweis: Prism ist zwar der Kontext für diesen Artikel. MVPVM funktioniert jedoch auch ohne Prism. Die einzelnen Komponenten werden einfach eng mit ihrer Implementierung verbunden statt lose – lose verbunden durch den Unity- oder Managed Extensibility Framework [MEF]-Abhängigkeitseinschleusungscontainer).

MVPVM: Das Modell

"Dies sind die Daten, auf deren Basis die Benutzeroberfläche ausgeführt wird. Es handelt sich in der Regel um ein Domänenobjekt, und diese Objekte sollten keine Kenntnis der Benutzeroberfläche haben." – "Twisting the Triad"

Die Trennung des Modells vom Ansichtsmodell ist erforderlich, um die Probleme der Abhängigkeitseinschleusung und die Verwendung innerhalb de Geschäftslogikschicht (Business Logic Layer, BLL) und der Datenzugriffsschicht (Data Access Layer, DAL) zu behandeln, um persistente Daten zu erstellen, zu lesen, zu aktualisieren, zu löschen und aufzulisten (Create, Read, Update, Delete and List, CRUDL). In MVPVM hat nur die DAL Zugriff auf die persistenten Modellobjekte.

MVPVM: Die Ansicht

Was das Verhalten angeht, stellt dieser Code dasselbe dar: Es ist die Aufgabe der Ansicht, die Inhalte eines Modells anzuzeigen. Vom Modell wird erwartet, dass es die entsprechenden Änderungsbenachrichtigungen auslöst, wenn seine Daten geändert werden. Diese ermöglichen der Ansicht, dem Modell entsprechend dem normalen Observer-Muster zu folgen. Wie im Fall von MVC, ermöglicht dies die Verbindung mehrerer Ansichten mit einem einzigen Modell." – "Twisting the Triad"

(Hinweis: Ich habe dieses Zitat verwendet, um hervorzuheben, dass MVP kein neues Muster ist und dass es heute noch genauso gültig ist wie zu dem Zeitpunkt, als MVP aus MVC entwickelt wurde. Dieses Zitat bezieht sich jedoch auf das "Model", während MVPVM "ViewModel" verwendet.)  

Im Fall von MVPVM ist niemals Code im Codebehind erforderlich. Der Presenter kann auf die Ansicht zugreifen und wie erforderlich Ereignisse abonnieren, Steuerelemente bearbeiten und die Benutzeroberfläche verändern. Dies hat sich bei der Entwicklung der Anwendung mit mehreren Zielen als nützlich erwiesen, die diesem Artikel beigefügt ist. Das Benutzerlistenfeld akzeptierte die Datenbindung für das geänderte Ereignis nicht, daher musste ich eine Möglichkeit entwickeln, die für alle drei Plattformen funktioniert (da diese den gleichen Code verwenden). Bei Aktivierung der Ansicht rufe ich die WireUpUserListBox auf (siehe Abbildung 7, Zeile 174) und verwende die Ansicht, um einen Verweis auf mein lstUserList-Steuerelement zu erhalten (dieses befindet sich in einer Datenvorlage). Wenn dieses gefunden wurde, stelle ich eine Verbindung zum SelectionChanged-Ereignis her und aktualisiere die Eigenschaft SelectedUser des Ansichtsmodells. Dies funktioniert für den Desktop, Silverlight und Windows Phone.

Benefits of Being Able to Access the View DirectlyAbbildung 7 Vorteile des direkten Zugriffs auf die Ansicht

Die Ansicht kennt das Ansichtsmodell nicht. Diese sind daher nicht eng miteinander verbunden. Solange das Ansichtsmodell alle Eigenschaften unterstützt, mit denen eine Ansicht verbunden ist, kann es die Ansicht auf einfache Weise verwenden. Es ist die Aufgabe des Presenters, die Ansichten mit ihren Ansichtsmodellen zu verbinden, indem er DataContext auf das entsprechende Ansichtsmodell festlegt.

MVPVM: Der Presenter

"Während es die Aufgabe der Ansicht ist, die Modelldaten anzuzeigen, steuert der Presenter, wie das Modell durch die Benutzeroberfläche bearbeitet und geändert werden kann. Dies ist das Kernstück der Verhaltensweisen einer Anwendung. In vielerlei Hinsicht entspricht der Presenter in MVP dem Anwendungsmodell in MVC. Der größte Teil des Codes, der die Funktionsweise der Benutzeroberfläche behandelt, befindet sich in der Presenter-Klasse. Der wesentliche Unterschied besteht darin, dass der Presenter direkt mit der mit ihm verknüpften Ansicht verbunden ist, sodass diese beiden eng zusammenarbeiten können, um die Benutzeroberfläche für ein bestimmtes Modell bereitzustellen." – "Twisting the Triad"

Der Presenter hat Abhängigkeiten auf den Schnittstellen für die BLLs, von denen er Domänenobjekte (Daten) abrufen muss, wie im linken Bereich in Abbildung 8 gezeigt. Er verwendet die aufgelösten Instanzen, wie im Modul (siehe Abbildung 8, Bereich unten rechts) oder im Bootstrapper konfiguriert, um auf die Daten zuzugreifen und das Ansichtsmodell auszufüllen.

Security Module CodeAbbildung 8 Code des Sicherheitsmoduls

In der Regel ist nur der Presenter eng in MVPVM-Komponenten verbunden. Er ist eng mit der Ansicht, dem Ansichtsmodell und den BLL-Schnittstellen verbunden. Presenter sind nicht für die Wiederverwendung vorgesehen. Sie bearbeiten bestimmte Aufgaben sowie die Geschäftslogik bzw. die Geschäftsregeln für diese Aufgaben. In den Fällen, in denen ein Presenter für verschiedene Unternehmensanwendungen wiederverwendet werden kann, wäre ein Modul wahrscheinlich besser für die Aufgabe geeignet, d. h., Sie könnten ein Anmeldemodul (Projekt) erstellen, das von allen Unternehmensanwendungen wiederverwendet werden kann. Wenn Sie für eine Schnittstelle kodieren, kann das Modul leicht wiederverwendet werden, indem Technologien für Abhängigkeitseinschleusungen wie MEF oder Unity verwendet werden.

MVPVM: Das Ansichtsmodell

"In MVP ist das Modell ein reines Domänenobjekt, und es gibt überhaupt keine Erwartungen hinsichtlich der Benutzeroberfläche (bzw. keine Verbindung zu dieser)." – "Twisting the Triad"

Dies verhindert, dass das Ansichtsmodell eng mit einer einzigen Ansicht verbunden ist, sodass es von zahlreichen Ansichten wiederverwendet werden kann. Das Ansichtsmodell verfügt auch nicht über eine Geschäftslogik für die Anwendung, sodass Ansichtsmodelle leicht von mehreren Unternehmensanwendungen verwendet werden können. Dies fördert die Wiederverwendung und die Anwendungsintegration. Betrachten Sie beispielsweise Abbildung 8, Bereich oben rechts, Zeile 28. Dieses SecurityCommandViewModel befindet sich im Projekt Gwn.Library.MvpVm.xxxx (mit xxxx = Silverlight, Desktop oder Windows Phone). Da die meisten Anwendungen ein Benutzeransichtsmodell erfordern, handelt es sich hier um eine wiederverwendbare Komponente. Ich muss sorgfältig darauf achten, keine für die Demoanwendung spezifische Geschäftslogik einzuführen. Dies stellt im Fall von MVPVM kein Problem dar, da die Geschäftslogik Aufgabe des Presenters, nicht des Ansichtsmodells ist (siehe Abbildung 6).

(Hinweis: Da die Eigenschaften des Ansichtsmodells vom Presenter ausgefüllt werden, lösen sie NotifyPropertyChanged-Ereignisse aus. Diese benachrichtigen die Ansicht darüber, dass neue Daten verfügbar sind. Es ist Aufgabe der Ansicht, die Aktualisierung auf die Daten des Ansichtsmodells bei Benachrichtigung selbst auszuführen.

Die Geschäftslogikschicht (Business Logic Layer, BLL)

BLLs kennen die Daten des persistenten Modells nicht. Sie funktionieren ausschließlich mit Domänenobjekten und Schnittstellen. In der Regel verwenden Sie die Abhängigkeitseinschleusung, um die DAL-Schnittstelle innerhalb der BLL aufzulösen, sodass Sie die DAL später austauschen können, ohne dass dies Auswirkungen auf den anschließenden Code hat. Beachten Sie in Abbildung 9, Zeile 34, dass ich für meine Demoanwendung eine MockBll-Implementierung für IBusinessLogicLayer verwende. Später kann ich diese einfach mittels einer einzigen Codezeile durch eine Produktionsimplementierung der Schnittstelle ersetzen, da ich auf der Basis einer Schnittstelle entwickelt habe.

Registering BLL, DAL and CommandsAbbildung 9 Registrieren der BLL, der DAL und von Befehlen

Die Geschäftslogik ist nicht auf BLLs begrenzt. In Abbildung 9 registriere ich benannte Typen (für IPresenterCommand), sodass ich die Abhängigkeitseinschleusung als Factory verwenden kann. Wenn der Benutzer auf eine Schaltfläche klickt (Zeilen 29-33 in Abbildung 10), wird der Befehlsparameter durch die Basisklasse aufgelöst (instantiiert), und der entsprechende Befehl wird ausgeführt. Beispielsweise handelt es sich bei LoginCommand (Abbildung 8, Bereich oben rechts) um einen Befehl, der die UserManagerView aktiviert. Hierfür waren lediglich ein Schaltflächenbefehl in XAML und ein Eintrag im Sicherheitsmodul erforderlich (siehe Abbildung 8, Bereich unten rechts, Zeile 32).

DataTemplate for the UserCommandViewModelAbbildung 10 DataTemplate für das UserCommandViewModel

Die Datenzugriffsschicht (Data Access Layer, DAL)

Persistente Domänendaten könnten in einer SQL-Datenbank, in XML oder in Textdateien gespeichert bzw. von einem REST-Dienst abgerufen werden. Im Fall von MVPVM enthält nur die DAL spezifische Informationen, die für das Abrufen von Daten erforderlich sind. Die DAL gibt nur Domänenobjekte an die BLL zurück. Dies verhindert, dass die BLL Verbindungszeichenfolgen, Dateihandles, REST-Dienste usw. kennen muss. Dadurch werden die Skalierbarkeit und die Erweiterbarkeit optimiert. Sie können für die DAL leicht von einer XML-Datei zu einem Cloud-Dienst wechseln, ohne dass dies Auswirkungen auf vorhandenen Code außerhalb der DAL und der Datei für die Anwendungskonfiguration hat. Wenn die Tests der neuen DAL-Einheit für die CRUDL-Prozesse funktionieren, können Sie die Anwendung für die Verwendung der neuen DAL konfigurieren, ohne dass dies Auswirkungen auf die aktuellen Anwendungen und deren Nutzung hat.

Verstehen der Geschichte, Nutzen von Erfahrungen

Dank dem Team für Muster und Verfahren konnte ich erfolgreich Projekte für meine Kunden durchführen, indem ich neueste Technologien und Muster wie MVPVM verwende, ohne jemals einen Fehlschlag zu erleiden. Ich habe festgestellt, dass ich Erfahrungen nutzen und mich leichter orientieren kann, wenn ich mit dem Tempo, den Technologien und den Mustern des Teams im Hinblick auf neuere Technologien und Muster Schritt halte.

MVP ist ein konstantes und konsistentes Muster, das ich seit den Anfangszeiten meiner Architekturstudien unter Verwendung von Projekten im Bereich Muster und Verfahren kenne. Um MVP vollständig verstehen zu können, müssen Sie die Geschichte verstehen, insbesondere die Komponenten von MVC. Dies ist keine leichte Aufgabe, da hier zahlreiche Faktoren eine Rolle spielen.

Wenn Sie jedoch die Vergangenheit und die Gründe für die Entwicklung verstehen, können Sie schneller die Notwendigkeit und Leistungsfähigkeit neuerer Muster erfassen. Auch im Fall von Prism und all seiner Komplexität und Magie können Sie die schwierige Lernkurve umgehen, indem Sie sich über die Geschichte informieren und die Notwendigkeit neuerer Muster erkennen. In all den erfolgreichen Projekten, an denen ich seit dem Auftreten dieses neuen Musters in frühen Versionen von Prism beteiligt war, habe ich mit MVPVM keine Probleme, Temposchwellen oder Ziegelwände (diese wurden anscheinend beseitigt) festgestellt. Im Gegenteil, Entwickler können nun schneller arbeiten, da wir Anwendungen erstellen, die skalierbar, erweiterbar und stabil sind.

Bill Kratochvil ist unabhängiger Auftragnehmer und leitender Technologie- und Architekturexperte eines Eliteentwicklerteams, das für ein führendes Unternehmen der Gesundheitsbranche an einem vertraulichen Projekt arbeitet. Der Hauptsitz seines eigenen Unternehmens – Global Webnet LLC – befindet sich im texanischen Amarillo.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Andy Bower, Christina Helton und Steve Sanderson