Saubere Architektur: Was ist das Ansichtsmodell?

13

In seinem Buch "Saubere Architektur" sagt Onkel Bob, dass der Präsentator die empfangenen Daten in etwas setzen sollte, das er "View Model" nennt.

Bildbeschreibung hier eingeben

Ist dies dasselbe wie das 'ViewModel' aus dem Entwurfsmuster Model-View-ViewModel (MVVM) oder handelt es sich um ein einfaches Datenübertragungsobjekt (Data Transfer Object, DTO)?

Wenn es sich nicht um ein einfaches DTO handelt, in welcher Beziehung steht es zur Ansicht? Erhält die Ansicht über eine Beobachterbeziehung Aktualisierungen?

Ich vermute, dass es eher wie das ViewModel von MVVM ist, denn in Kapitel 23 seines Buches sagt Robert Martin:

Die Aufgabe des [Präsentators] besteht darin, Daten aus der Anwendung zu akzeptieren und für die Präsentation zu formatieren, sodass die Ansicht sie einfach auf den Bildschirm verschieben kann. Wenn die Anwendung beispielsweise möchte, dass ein Datum in einem Feld angezeigt wird, übergibt sie dem Presenter ein Datumsobjekt. Der Presenter formatiert diese Daten dann in eine entsprechende Zeichenfolge und platziert sie in einer einfachen Datenstruktur namens View-Modell, in der View sie finden kann.

Dies impliziert, dass die Ansicht in irgendeiner Weise mit dem ViewModel verbunden ist, anstatt sie einfach als Funktionsargument zu erhalten (wie dies bei einem DTO der Fall wäre).

Ein weiterer Grund, warum ich denke, liegt darin, dass der Presenter beim Betrachten des Bildes das Ansichtsmodell verwendet, nicht jedoch die Ansicht. Während der Präsentator sowohl die Ausgabegrenze als auch die Ausgabedaten-DTO verwendet.

Wenn es sich weder um ein DTO noch um das ViewModel von MVVM handelt, erläutern Sie bitte, was es ist.

Fearnbuster
quelle
Ich denke die Antwort ist "es kommt darauf an". Wenn es sich um eine Webanwendung handelt, ist ein Ansichtsmodell im Grunde genommen ein DTO, da es letztendlich als HTML-Zeichenfolge serialisiert wird. Andernfalls ist ein Ansichtsmodell nur ein spezialisiertes Objekt zum Anzeigen der Daten in der Ansicht.
Greg Burghardt
In MVVM (WPF, WinForms - Anwendungen) ViewModelist Wrapper für Controller, Presenterund ViewModelin Rein Architektur Onkel Bob.
Fabio
@ Greg Burghardt - Wenn das ViewModel eine spezialisierte Datenstruktur ist, wie wird die View über Änderungen informiert?
Fearnbuster
@Fabio - Wenn ich Ihre Aussage richtig verstehe, dass das ViewModel im MVVM-Muster allen Komponenten entspricht, die sich in der Gruppe ganz links im Diagramm befinden? Wenn dies für die Architektur von Onkel Bob zutrifft, warum listet er dann den Controller und den Presenter getrennt auf?
Fearnbuster
Ich denke, er hat Eingabe- und Ausgabehandler in verschiedene Objekte / Klassen unterteilt. In MVVM könnte es Controller-> ICommandund Presenter-> sein data-binding mechanism.
Fabio

Antworten:

17

Entspricht dies dem ViewModel aus dem MVVM-Entwurfsmuster (Model-View-ViewModel)?

Nee.

Das wäre dies :

Bildbeschreibung hier eingeben

Das hat Zyklen. Onkel Bob hat Zyklen sorgfältig vermieden .

Stattdessen haben Sie Folgendes:

Bildbeschreibung hier eingeben

Welches sicherlich keine Zyklen hat. Sie fragen sich jedoch, woher die Ansicht ein Update kennt. Wir werden gleich darauf zurückkommen.

oder ist es ein einfaches Data Transfer Object (DTO)?

So zitieren Sie Bob von der vorherigen Seite:

Wenn Sie möchten, können Sie grundlegende Strukturen oder einfache Datenübertragungsobjekte verwenden. Oder Sie können es in eine Hashmap packen oder in ein Objekt konstruieren.

Saubere Architektur p207

Also sicher, wenn du magst.

Aber ich nehme stark an, was wirklich nervt Sie ist dies :

Bildbeschreibung hier eingeben

Dieser niedliche kleine Missbrauch von UML stellt die Richtung der Quellcode-Abhängigkeit der Richtung des Kontrollflusses gegenüber. Hier finden Sie die Antwort auf Ihre Frage.

In einer Nutzungsbeziehung:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Der Steuerungsfluss geht in dieselbe Richtung wie die Quellcode-Abhängigkeit.

In einer Implementierungsbeziehung:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Der Steuerungsfluss verläuft normalerweise in die entgegengesetzte Richtung wie die Quellcode-Abhängigkeit.

Was bedeutet, dass du dir das wirklich ansiehst:

Bildbeschreibung hier eingeben

Sie sollten in der Lage sein zu sehen, dass der Kontrollfluss niemals vom Präsentator zur Ansicht gelangen wird.

Wie kann das sein? Was bedeutet es?

Dies bedeutet, dass die Ansicht entweder einen eigenen Thread hat (was nicht so ungewöhnlich ist) oder (wie @Euphoric hervorhebt), dass der Kontrollfluss von etwas anderem in die Ansicht eintritt, das hier nicht dargestellt ist.

Wenn es sich um denselben Thread handelt, weiß die Ansicht, wann das Ansichtsmodell zum Lesen bereit ist. Wenn dies jedoch der Fall ist und die Ansicht eine grafische Benutzeroberfläche ist, fällt es schwer, den Bildschirm neu zu zeichnen, wenn der Benutzer ihn bewegt, während er auf die Datenbank wartet.

Wenn die Ansicht über einen eigenen Thread verfügt, verfügt sie über einen eigenen Steuerungsfluss. Das heißt, um dies zu implementieren, muss die Ansicht das Ansichtsmodell abfragen, um Änderungen zu bemerken.

Da der Präsentator nicht weiß, dass die Ansicht vorhanden ist, und der Präsentator nicht weiß, dass die Ansicht vorhanden ist, können sie sich überhaupt nicht gegenseitig anrufen. Sie können sich nicht gegenseitig angreifen. Alles, was passieren kann, ist, dass der Präsentator in das Ansichtsmodell schreibt und die Ansicht das Ansichtsmodell liest. Wann immer es sich anfühlt.

Nach diesem Diagramm ist das einzige, was View und Presenter gemeinsam haben, die Kenntnis des View-Modells. Und es ist nur eine Datenstruktur. Erwarten Sie also kein Verhalten.

Das mag unmöglich erscheinen, aber es kann auch dann zum Funktionieren gebracht werden, wenn das Ansichtsmodell komplex ist. Ein kleines aktualisiertes Feld ist alles, was die Ansicht abfragen müsste, um eine Änderung zu erkennen.

Jetzt können Sie natürlich darauf bestehen, das Beobachter-Muster zu verwenden, oder Sie müssen sich dieses Problem durch ein Framework verbergen, aber bitte haben Sie Verständnis dafür, dass Sie es nicht müssen.

Hier ist ein bisschen Spaß, den ich hatte, um den Kontrollfluss zu veranschaulichen:

Bildbeschreibung hier eingeben

Beachten Sie, dass Sie immer dann, wenn Sie sehen, dass der Fluss gegen die zuvor definierten Richtungen verläuft, einen Rückruf erhalten. Dieser Trick hilft uns nicht, zur Ansicht zu gelangen. Nun, es sei denn, wir kehren zuerst zu dem zurück, was der Controller genannt wird. Oder Sie können einfach das Design ändern , um zur Ansicht zu gelangen. Das behebt auch das Problem, dass mit Data Access und seiner Benutzeroberfläche ein Jojo-Problem begonnen hat .

Das einzige andere, was Sie hier lernen müssen, ist, dass der Use Case Interactor so ziemlich alles in beliebiger Reihenfolge aufrufen kann, solange er den Moderator zuletzt anruft.

kandierte_orange
quelle
Vielen Dank für die Antwort Mann, ich habe Ihre Antworten auf verschiedene andere Fragen über die saubere Architektur gesehen. Schlagen Sie vor, dass die Ansicht ständig ein Flag prüft, z. B. innerhalb des Ansichtsmodells, um festzustellen, ob Änderungen vorgenommen wurden? Müsste die Ansicht dann das gesamte Ansichtsmodell erneut anzeigen, oder sollte ich eine Reihe verschachtelter Flags verwenden, um anzugeben, welche Daten geändert wurden?
Fearnbuster
Die Ansicht muss nicht ständig abgefragt werden. Zum Beispiel fragt Web 1.0 nur ab, wenn der Benutzer auf "Neu laden" klickt. Das ständige Abrufen ist eine Entwurfsentscheidung, die die tatsächlichen Bedürfnisse der Benutzer berücksichtigen sollte. Ich sage nur, dass es möglich ist. Der Sinn eines Update-Feldes besteht darin, ein Update schnell zu erkennen. Wird nur benötigt, wenn das Ansichtsmodell komplex ist. Überlegen Sie auch, was passiert, wenn in der Ansicht angezeigt wird, während sich der Präsentator in der Mitte eines Updates befindet.
candied_orange
Okay, vielen Dank für die Hilfe. Wenn / wenn Sie dieser Architektur folgen, ist dies die Technik, die Sie normalerweise verwenden?
Fearnbuster
1
Ich denke, es ist ein großer Fehler, dass diese Antwort Designabhängigkeit und Laufzeitabhängigkeit gruppiert. Die beiden können unterschiedlich sein.
Euphoric
1
@Euphoric Warum danke. Ich binde sie zusammen, denn wenn Sie keine Quellcode-Abhängigkeit von etwas haben, können Sie für nichts einen Laufzeitverweis darauf verwenden, da Sie nicht verstehen, was es ist. Alles, was Sie tun können, ist die Referenz zu speichern, wie dies bei einer Sammlung der Fall ist. Wenn das ein Fehler ist, würde ich es gerne verstehen.
candied_orange
2

Ich finde dieses Problem zu verwirrend und es würde viel Text und Zeit in Anspruch nehmen, um das Problem richtig zu erklären, da ich glaube, dass Sie sowohl Martins saubere Architektur als auch MVVM falsch verstehen.

Als erstes ist zu beachten, dass das von Ihnen veröffentlichte Diagramm unvollständig ist. Es zeigt nur "Geschäftslogik", aber es fehlt eine Art "Orchestrator", der die Teile tatsächlich in der richtigen Reihenfolge bewegt. Bildbeschreibung hier eingeben

Der Code des Orchestrators wäre so einfach wie

string Request(string request) // returns response
{
    Controller.Run(data);
    Presenter.Run();
    return View.Run();
}

Ich glaube, ich habe Martin darüber in einem seiner Vorträge über saubere Architektur sprechen hören.

Eine andere Sache, auf die man hinweisen sollte, ist, dass die Bemerkung von candied_orange über fehlende Zyklen falsch ist. Ja, das Cycled existiert (und sollte nicht) in der Architektur des Codes. Zyklen zwischen Laufzeitinstanzen sind jedoch häufig und führen häufig zu einem einfacheren Entwurf.

Das ist in MVVM der Fall. In MVVM View ist ViewModel abhängig, und ViewModel benachrichtigt View mithilfe von Ereignissen über Änderungen. Dies bedeutet, dass beim Entwurf der Klassen nur Abhängigkeiten von View- zu Model-Klassen bestehen, während zur Laufzeit jedoch zyklische Abhängigkeiten zwischen View- und ViewModel-Instanzen bestehen. Aus diesem Grund ist Orchestrator nicht erforderlich, da ViewModel die Möglichkeit bietet, in View herauszufinden, wann sich das Update von selbst durchführen lässt. Aus diesem Grund verwenden "Benachrichtigungen" in diesem Diagramm "schnörkellose" und keine direkte Linie. Dies bedeutet, dass View Änderungen in ViewModel beobachtet, nicht, dass ViewModel von View abhängt.

Bildbeschreibung hier eingeben

Das Wichtigste, was Sie aus Martins Clean Architecture herausholen sollten, ist nicht das Design selbst, sondern der Umgang mit Abhängigkeiten. Einer der kritischen Punkte, die er in seinen Gesprächen hervorhebt, ist, dass, wenn es eine Grenze gibt, alle Code-Abhängigkeiten, die diese Grenze überschreiten, diese in einer Richtung überschreiten. Im Diagramm wird diese Grenze durch eine Doppellinie dargestellt. Und es gibt viele Abhängigkeit Inversion durch Schnittstellen ( InputBoundary, OutputBoundaryund DataAccessInterface) dass Korrekturen der Code Abhängigkeitsrichtung.

Im Gegensatz dazu ist die ViewModelin Clean Architecture nur einfaches DTO ohne Logik. Dies wird durch das <DS>Tag deutlich. Und dies ist der Grund, warum dies orchestratornotwendig ist, da Viewnicht bekannt ist, wann die Logik ausgeführt werden soll.

Wenn ich das Diagramm so "abflachen" würde, dass es zur Laufzeit wie folgt aussieht:

Bildbeschreibung hier eingeben

Während der Laufzeit sind die Abhängigkeiten also in die "falsche" Richtung, aber das ist in Ordnung.

Ich empfehle, sich seinen Vortrag über Clean Architecture anzuschauen , um seine Argumentation besser zu verstehen.

Euphorisch
quelle
Ihr "Orchestrator" sollte den Moderator nicht anrufen. Der Use Case Interactor macht das.
candied_orange
@candied_orange Stimmt, das ist ein Fehler.
Euphoric
Danke für die Antwort, es ist immer gut, ein paar unterschiedliche Meinungen zu bekommen. Ich habe die Antworten Ihrer beiden Jungs positiv bewertet. Weiß einer von Ihnen zufällig, ob Robert Martin irgendwo eine Codebasis hat, in der er eine Form seiner Architektur implementiert hat? Ich habe mir sein FitNess-Projekt angesehen, aber ich konnte den Wald vor lauter Bäumen nicht sehen. Auch wenn das Bild, das ich gepostet habe, das Diagramm ist, das Onkel Bob immer in seinen Gesprächen verwendet, ist es tatsächlich nur ein Beispiel dafür, wie Ihre Architektur aussehen KANN. Während es sehr unterschiedlich aussehen kann, solange der Abhängigkeitsfluss korrekt ist?
Fearnbuster
@Fearnbuster Auf deine letzte Frage ja. Die Richtung der Abhängigkeiten ist wichtiger als die Struktur. Ich glaube, dass "saubere Architektur", "Zwiebelarchitektur" und "hexagonale Architektur" wirklich Implementierungen derselben Idee von "Abhängigkeiten in Schach halten" sind.
Euphoric
@Euphoric Ehrlich gesagt würde ich sagen, dass dies wahrscheinlich der Fall ist, da er in einem anderen Bild seines Buches (Abbildung 8.2 von Kapitel 8) eine Architektur zeigt, die anders aussieht. In diesem Diagramm ist der Controller tatsächlich ein Mittler zwischen dem Interactor und dem Presenter. Es gibt auch keine Ausgabegrenze für den Interaktor. Es scheint, dass der Interactor Anforderungen über eine Schnittstelle empfängt und die Antworten dann über dieselbe Schnittstelle zurückgibt (ich gehe davon aus, dass dies über einen einfachen Funktionsrückgabewert erfolgt, da ich mir keinen anderen Mechanismus vorstellen kann, der auf diese Weise funktionieren würde).
Fearnbuster