Zusammenfassung
- Die Verwendung aller Muster ist situativ und der Vorteil (falls vorhanden) liegt immer in der verringerten Komplexität.
- MVVM zeigt uns, wie Sie Verantwortlichkeiten auf Klassen in einer GUI-Anwendung verteilen.
- ViewModel projiziert die Daten aus dem Modell in ein Format, das zur Ansicht passt.
- Für triviale Projekte ist MVVM nicht erforderlich. Es reicht aus, nur die Ansicht zu verwenden.
- Bei einfachen Projekten ist die Aufteilung von ViewModel / Modell möglicherweise nicht erforderlich, und die Verwendung eines Modells und einer Ansicht ist ausreichend.
- Model und ViewModel müssen nicht von Anfang an vorhanden sein und können bei Bedarf eingeführt werden.
Wann man Muster verwendet und wann man sie vermeidet
Für eine ausreichend einfache Anwendung ist jedes Entwurfsmuster übertrieben. Angenommen, Sie schreiben eine GUI-Anwendung, die eine einzelne Schaltfläche anzeigt, die beim Drücken "Hallo Welt" anzeigt. In diesem Fall erhöhen Entwurfsmuster wie MVC, MVP und MVVM die Komplexität erheblich, ohne jedoch einen Mehrwert zu bieten.
Im Allgemeinen ist es immer eine schlechte Entscheidung, ein Designmuster einzuführen, nur weil es etwas passt. Entwurfsmuster sollten verwendet werden, um die Komplexität zu reduzieren, entweder indem die Gesamtkomplexität direkt reduziert wird oder indem ungewohnte Komplexität durch vertraute Komplexität ersetzt wird. Wenn das Entwurfsmuster die Komplexität auf keine dieser beiden Arten reduzieren kann, verwenden Sie es nicht.
Um die vertraute und unbekannte Komplexität zu erklären, nehmen Sie die folgenden 2 Zeichenfolgen:
- "D. € | Ré% dfà? C"
- "CorrectHorseBatteryStaple"
Während die zweite Zeichenfolge doppelt so lang ist wie die erste, ist sie leichter zu lesen, schneller zu schreiben und leichter zu merken als die erste Folge, weil sie vertrauter ist. Gleiches gilt für bekannte Muster im Code.
Dieses Problem gewinnt eine andere Dimension, wenn Sie bedenken, dass die Vertrautheit vom Leser abhängt. Einige Leser werden feststellen, dass "3.14159265358979323846264338327950" leichter zu merken ist als eines der oben genannten Passwörter. Einige werden nicht. Wenn Sie also eine MVVM-Variante verwenden möchten, versuchen Sie, eine zu verwenden, die die häufigste Form in der von Ihnen verwendeten Sprache und dem verwendeten Framework widerspiegelt.
MVVM
Lassen Sie uns anhand eines Beispiels auf das Thema MVVM eingehen. MVVM zeigt uns, wie Verantwortlichkeiten zwischen Klassen in einer GUI-Anwendung (oder zwischen Ebenen - dazu später mehr) verteilt werden, mit dem Ziel, eine kleine Anzahl von Klassen zu haben und gleichzeitig die Anzahl der Verantwortlichkeiten pro Klasse klein und klar zu halten.
'Richtig' MVVM setzt zumindest eine mäßig komplexe Anwendung voraus, die sich mit Daten befasst, die von "irgendwo" stammen. Es kann die Daten aus einer Datenbank, einer Datei, einem Webdienst oder aus einer Vielzahl anderer Quellen abrufen.
Beispiel
In unserem Beispiel haben wir 2 Klassen View
und Model
, aber nein ViewModel
. Der Model
Wraps eine CSV-Datei, die er beim Start liest und beim Herunterfahren der Anwendung speichert, mit allen Änderungen, die der Benutzer an den Daten vorgenommen hat. Dies View
ist eine Window-Klasse, die die Daten aus Model
einer Tabelle anzeigt und es dem Benutzer ermöglicht, die Daten zu bearbeiten. Der CSV-Inhalt könnte ungefähr so aussehen:
ID, Name, Price
1, Stick, 5$
2, Big Box, 10$
3, Wheel, 20$
4, Bottle, 3$
Neue Anforderungen: Preis in Euro anzeigen
Jetzt werden wir gebeten, eine Änderung an unserer Anwendung vorzunehmen. Die Daten bestehen aus einem zweidimensionalen Raster, das bereits eine "Preisspalte" enthält, die einen Preis in USD enthält. Wir müssen eine neue Spalte hinzufügen, in der die Preise in Euro zusätzlich zu denen in USD angezeigt werden, basierend auf einem vordefinierten Wechselkurs. Das Format der CSV-Datei darf sich nicht ändern, da andere Anwendungen mit derselben Datei arbeiten und diese anderen Anwendungen nicht unter unserer Kontrolle stehen.
Eine mögliche Lösung besteht darin, die neue Spalte einfach zur Model
Klasse hinzuzufügen . Dies ist nicht die beste Lösung, da Model
hierdurch alle Daten gespeichert werden, die der CSV zur Verfügung gestellt werden - und wir keine neue Euro-Preisspalte in der CSV wünschen. Die Änderung an Model
wäre also nicht trivial, und es wäre auch schwieriger zu beschreiben, was die Model-Klasse tut, was ein Code-Geruch ist .
Wir könnten die Änderung auch in der vornehmen View
, aber unsere aktuelle Anwendung verwendet Datenbindung, um die Daten direkt anzuzeigen, wie von unserer Model
Klasse bereitgestellt . Da unser GUI-Framework es uns nicht erlaubt, eine zusätzliche berechnete Spalte in eine Tabelle einzufügen, wenn die Tabelle an eine Datenquelle gebunden ist, müssten wir eine wesentliche Änderung View
an vornehmen, damit dies funktioniert, was die View
Komplexität erheblich erhöht .
Einführung in das ViewModel
Es gibt keine ViewModel
in der Anwendung, da Model
die Daten bis jetzt genau so dargestellt werden, wie es die CSV benötigt, und auch so, wie sie View
benötigt werden. Ein ViewModel
Zwischenraum zu haben, wäre ohne Zweck komplexer geworden. Aber jetzt, da die Model
Daten nicht mehr so dargestellt werden, wie sie View
benötigt werden, schreiben wir eine ViewModel
. Das ViewModel
projiziert die Daten der Model
so, dass die View
einfach sein können. Zuvor hat die View
Klasse die Klasse abonniert Model
. Jetzt ViewModel
abonniert die neue Klasse die Model
Klasse und stellt die Model
Daten der Klasse der View
- zur Verfügung. In einer zusätzlichen Spalte wird der Preis in Euro angezeigt. Das View
weiß der nicht mehrModel
Jetzt kennt es nur noch das ViewModel
, was vom Standpunkt des View
Aussehens aus genauso aussieht Model
wie zuvor - außer dass die exponierten Daten eine neue schreibgeschützte Spalte enthalten.
Neue Anforderungen: andere Art, die Daten zu formatieren
Die nächste Kundenanforderung lautet, dass wir die Daten nicht als Zeilen in einer Tabelle anzeigen sollen, sondern stattdessen die Informationen jedes Elements (auch als Zeile bezeichnet) als Karte / Box anzeigen und 20 Boxen in einem 4x5-Raster auf dem Bildschirm anzeigen sollen, wobei 20 angezeigt werden Boxen auf einmal. Weil wir die Logik des View
Einfachen beibehalten haben , ersetzen wir das Einfache View
vollständig durch eine neue Klasse, die den Wünschen des Kunden entspricht. Natürlich gibt es einen anderen Kunden, der den alten bevorzugt View
, also müssen wir jetzt beide unterstützen. Da die gesamte gängige Geschäftslogik bereits vorhanden ViewModel
ist, ist dies kein großes Problem. Wir können dieses Problem lösen, indem wir die View-Klasse in umbenennen TableView
und eine neue schreibenCardView
Klasse, die die Daten in einem Kartenformat anzeigt. Wir müssen auch einen Klebercode schreiben, der möglicherweise ein Oneliner in der Startfunktion ist.
Neue Anforderungen: dynamischer Wechselkurs
Die nächste Kundenanfrage ist, dass wir den Wechselkurs aus dem Internet beziehen, anstatt einen vordefinierten Wechselkurs zu verwenden. Dies ist der Punkt, an dem wir meine frühere Aussage über "Ebenen" erneut betrachten. Wir ändern unsere Model
Klasse nicht, um einen Wechselkurs bereitzustellen. Stattdessen schreiben (oder finden) wir eine völlig unabhängige zusätzliche Klasse, die den Wechselkurs liefert. Diese neue Klasse wird Teil der Modellebene, und wir ViewModel
konsolidieren die Informationen des CSV-Modells und des Wechselkursmodells, die sie dann dem Modell präsentieren View
. Für diese Änderung müssen die alte Model-Klasse und die View-Klasse nicht einmal berührt werden. Nun, wir müssen die Model-Klasse in umbenennen CsvModel
und rufen die neue Klasse auf ExchangeRateModel
.
Wenn wir das ViewModel nicht eingeführt hätten, sondern stattdessen bis jetzt darauf gewartet hätten, wäre der Arbeitsaufwand für die Einführung des ViewModel jetzt höher, da wir sowohl vom als auch vom View
und zum Model
Verschieben erhebliche Mengen an Funktionen entfernen müssen die Funktionalität in die ViewModel
.
Nachwort zu Unit Tests
Der Hauptzweck von MVVM besteht nicht darin, dass der Code im Modell und im ViewModel unter Unit Test gestellt werden kann. Der Hauptzweck von MVVM besteht darin, dass der Code in Klassen mit einer kleinen Anzahl genau definierter Verantwortlichkeiten unterteilt wird. Einer von mehreren Vorteilen eines Codes, der aus Klassen mit einer geringen Anzahl genau definierter Verantwortlichkeiten besteht, besteht darin, dass es einfacher ist, den Code einem Unit-Test zu unterziehen. Ein viel größerer Vorteil ist, dass der Code leichter zu verstehen, zu warten und zu ändern ist.