Gedanken zur Implementierung von Model-View-Presenter


34

Ich versuche zu verstehen, wie eine gute Entkopplung zwischen einer Benutzeroberfläche und dem Modell implementiert werden kann, habe aber Probleme, genau herauszufinden, wo die Linien aufgeteilt werden müssen.

Ich habe mir Model-View-Presenter angesehen, bin mir aber nicht sicher, wie ich es implementieren soll. Beispielsweise verfügt meine Ansicht über mehrere Dialogfelder.

  • Sollte es eine View-Klasse mit Instanzen der einzelnen Dialogfelder geben? Wie sollten dann in diesem Fall die Dialoge mit dem Präsentator interagieren? dh Wenn ein einzelner Dialog Daten vom Modell über den Presenter anfordern muss, wie soll der Dialog einen Verweis auf den Presenter erhalten? Über einen Verweis auf die Ansicht, die ihr beim Bau gegeben wurde?
  • Ich dachte, vielleicht sollte die Ansicht eine statische Klasse sein? Dann die Dialoge GetView und dort den Presenter holen ...
  • Ich hatte darüber nachgedacht, den Presenter mit dem Besitz der Ansicht und des Modells einzurichten (im Gegensatz dazu, dass die Ansicht den Presenter und den Presenter mit dem Modell hat) und der Presenter Rückrufe für Ereignisse in der Ansicht registriert, aber das lässt es sehr erscheinen mehr gekoppelt (oder zumindest sprachabhängig)

Ich versuche zu:

  1. machen Sie dies so entkoppelt wie möglich
  2. Im Idealfall ist es möglich, den Presenter / das Model mit Ansichten anderer Sprachen zu koppeln. (Ich habe nicht viele Dinge zwischen den Sprachen erledigt, aber ich weiß, dass es möglich ist, insbesondere, je mehr void(void)ich mich an eine C # -Anwendung halten kann.) C ++ Bibliothek ...
  3. Halten Sie den Code sauber und einfach

Also .. irgendwelche Vorschläge, wie mit den Interaktionen umgegangen werden soll?


Haben Sie sich diesen Artikel angesehen ?: en.wikipedia.org/wiki/Model-view-presenter
Bernard

1
Ich habe es ein bisschen schnell und auf hohem Niveau gefunden. Ich
möchte

Antworten:


37

Willkommen auf einer rutschigen Piste. Sie haben zu diesem Zeitpunkt festgestellt, dass es eine endlose Vielfalt aller Modellsicht-Interaktionen gibt. MVC, MVP (Taligent, Dolphin, Passive View), MVVM, um nur einige zu nennen.

Das Model View Presenter-Muster ist wie die meisten Architekturmuster vielfältig und experimentierfreudig. Allen Variationen gemeinsam ist die Rolle des Moderators als "Mittelsmann" zwischen Sicht und Modell. Die beiden häufigsten sind die Passive View und der Supervising Presenter / Controller - [ Fowler ]. Passive View behandelt die Benutzeroberfläche als eine sehr flache Schnittstelle zwischen dem Benutzer und dem Präsentator. Es enthält sehr wenig oder gar keine Logik und überträgt so viel Verantwortung an einen Moderator. Supervising Presenter / Controllerversucht, die in vielen UI-Frameworks integrierte Datenbindung zu nutzen. Die Benutzeroberfläche verwaltet die Datensynchronisation, aber der Präsentator / Controller sorgt für eine komplexere Logik. In beiden Fällen bilden das Modell, die Ansicht und der Präsentator eine Triade

Dafür gibt es viele Möglichkeiten. Es ist sehr verbreitet, dies zu sehen, indem jedes Dialogfeld / Formular als eine andere Ansicht behandelt wird. Oft besteht eine 1: 1-Beziehung zwischen Ansichten und Moderatoren. Dies ist keine feste Regel. Es ist durchaus üblich, dass ein Moderator mehrere verwandte Ansichten verwaltet oder umgekehrt. Es hängt alles von der Komplexität der Ansicht und der Komplexität der Geschäftslogik ab.

Wie Ansichten und Moderatoren einen Bezug zueinander herstellen, wird manchmal als Verkabelung bezeichnet . Sie haben drei Möglichkeiten:

Die Ansicht enthält einen Verweis auf den Präsentator.
Ein Formular oder ein Dialogfeld implementiert eine Ansicht. Das Formular verfügt über Ereignisbehandlungsroutinen, die über direkte Funktionsaufrufe einen Moderator ansprechen:

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

Da der Präsentator keinen Verweis auf die Ansicht hat, muss die Ansicht die Daten als Argumente senden. Der Präsentator kann über Ereignisse / Rückruffunktionen, auf die die Ansicht achten muss, mit der Ansicht kommunizieren.

Der Presenter enthält einen Verweis auf die Ansicht.
In diesem Szenario werden in der Ansicht die Eigenschaften der Daten angezeigt, die dem Benutzer angezeigt werden. Der Präsentator wartet auf Ereignisse und bearbeitet die Eigenschaften der Ansicht:

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

Beide haben einen Bezug zueinander und bilden eine kreisförmige Abhängigkeit.
Dieses Szenario ist tatsächlich einfacher zu bearbeiten als die anderen. Die Ansicht reagiert auf Ereignisse, indem sie Methoden im Presenter aufruft. Der Präsentator liest / ändert Daten aus der Ansicht über offen gelegte Eigenschaften.

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

Bei den MVP-Mustern müssen noch andere Probleme berücksichtigt werden. Erstellungsreihenfolge, Objektlebensdauer, wo die Verkabelung stattfindet, Kommunikation zwischen MVP-Triaden, aber diese Antwort ist schon lange genug gewachsen.


1
Das ist auf jeden Fall hilfreich. Die Kommunikation zwischen den Triaden und dem Leben ist der Moment, in dem ich Probleme habe, da ich etwas davon verstehe.
Trycatch

8

Wie jeder gesagt hat, gibt es Dutzende von Meinungen und keine einzige davon ist richtig oder falsch. Ohne auf die Vielzahl von Mustern einzugehen und sich nur auf MVP zu konzentrieren, hier einige Vorschläge zur Implementierung.

Halte sie getrennt. Die Ansicht sollte eine Schnittstelle implementieren, die die Verbindung zwischen der Ansicht und dem Präsentator herstellt. Die Ansicht erstellt einen Präsentator und injiziert sich in den Präsentator und macht die Methoden verfügbar, die der Präsentator für die Interaktion mit der Ansicht bietet. Die Ansicht ist dafür verantwortlich, diese Methoden oder Eigenschaften nach Belieben zu implementieren. Im Allgemeinen haben Sie eine Ansicht: einen Moderator, aber in einigen Fällen können Sie mehrere Ansichten haben: einen Moderator (Web, WPF usw.). Der Schlüssel hierbei ist, dass der Präsentator nichts über UI-Implementierungen weiß und nur über die Benutzeroberfläche mit der Ansicht interagiert.

Hier ist ein Beispiel. Zuerst haben wir eine Ansichtsklasse mit einer einfachen Methode, um dem Benutzer eine Nachricht anzuzeigen:

interface IView
{
  public void InformUser(string message);
}

Hier ist der Moderator. Beachten Sie, dass der Präsentator eine IView in seinen Konstruktor aufnimmt.

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

Hier ist die eigentliche Benutzeroberfläche. Dies kann ein Fenster, ein Dialog, eine Webseite usw. sein. Beachten Sie, dass der Konstruktor für die Ansicht den Präsentator erstellt, indem er sich selbst einfügt.

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

Der Präsentator kümmert sich nicht darum, wie die Ansicht die Methode implementiert, die sie gerade ausführt. Soweit der Moderator weiß, wird möglicherweise in eine Protokolldatei geschrieben und diese dem Benutzer nicht einmal angezeigt.

In jedem Fall arbeitet der Präsentator am Backend mit dem Modell und möchte den Benutzer irgendwann darüber informieren, was los ist. Jetzt haben wir also irgendwo im Presenter eine Methode, die die InformUser-Nachricht views aufruft.

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

Hier bekommen Sie Ihre Entkopplung. Der Moderator hat nur einen Verweis auf eine Implementierung von IView und kümmert sich nicht wirklich darum, wie diese implementiert ist.

Dies ist auch eine schlechte Implementierung von Mans, da Sie in der Ansicht einen Verweis auf den Presenter haben und Objekte über Konstruktoren festgelegt werden. In einer robusteren Lösung möchten Sie wahrscheinlich die Inversion von Steuerungscontainern (IoC) wie Windsor, Ninject usw. untersuchen, die die Implementierung von IView zur Laufzeit auf Abruf für Sie auflösen und damit noch weiter entkoppeln.


4

Ich denke, es ist wichtig, sich daran zu erinnern, dass der Controller / Presenter der Ort ist, an dem die Aktion wirklich stattfindet. Eine Kopplung im Controller ist zwangsläufig erforderlich.

Der Kernpunkt des Controllers besteht darin, dass sich das Modell nicht ändern muss, wenn Sie eine Änderung an der Ansicht vornehmen, und umgekehrt (wenn sich das Modell ändert, muss auch die Ansicht nicht geändert werden), da der Controller das übersetzt Modell in die Ansicht und wieder zurück. Der Controller ändert sich jedoch, wenn Modell- oder Ansichtsänderungen vorgenommen werden, da Sie innerhalb des Controllers effektiv übersetzen müssen, wie das Modell angezeigt werden soll, um Änderungen in der Ansicht wieder in den Modus zu übernehmen.

Das beste Beispiel, das ich geben kann, ist, dass ich beim Schreiben einer MVC-App nicht nur Daten in der GUI-Ansicht haben kann, sondern auch eine Routine schreiben kann, die Daten, die aus dem Modell gezogen wurden, in eine Routine schiebt string, die im Debugger angezeigt werden soll (und als Erweiterung in eine reine Textdatei). Wenn ich Modelldaten nehmen und frei in Text übersetzen kann, ohne die Ansicht oder das Modell und nur den Controller zu ändern , bin ich auf dem richtigen Weg.

Abgesehen davon müssen Sie Referenzen zwischen den verschiedenen Komponenten haben, damit alles funktioniert. Der Controller muss die Ansicht kennen, um Daten zu übertragen, und der View muss die Ansicht kennen, um sie zu informieren, wenn eine Änderung vorgenommen wurde (z. B. wenn der Benutzer auf "Speichern" oder "Neu ..." klickt). Der Controller muss über das Modell Bescheid wissen, um die Daten abzurufen, aber ich würde argumentieren, dass das Modell über nichts anderes Bescheid wissen sollte.

Einschränkung: Ich komme aus einem komplett Mac, Objective-C, Cocoa-Hintergrund, der Sie wirklich in das MVC-Paradigma einführt, ob Sie wollen oder nicht.


Das ist definitiv mein Ziel. Mein Hauptproblem ist, wie man die Ansicht einrichtet - ob es eine Klasse mit einer Instanz jedes Dialogs sein soll, und dann View.Getters verwendet, die Dialog.Getters aufrufen, oder ob der Präsentator Dialog.Getters direkt aufrufen kann ( dies scheint zu eng miteinander verbunden zu sein, also wahrscheinlich nicht?)
trycatch

Ich denke, der Presenter / Controller sollte für die Views voll verantwortlich sein, also letztere. Auch hier muss ein gewisses Maß an Kopplung auftreten, aber zumindest wenn die Richtung der Verantwortung klar ist, sollte die Wartung auf lange Sicht einfacher sein.
Philip Regan

2
Ich bin mir sicher einig, dass das P / C für die Ansicht verantwortlich sein sollte, aber ich dachte, ein Teil, der MVP mächtig machen sollte, war die Fähigkeit, die gesamte UI-Bibliothek herauszunehmen und eine neue anzuschließen und mit etwas Massieren (dllimportieren und so weiter) in der Lage sein, einen anderen an seiner Stelle laufen zu lassen. Wäre dies nicht schwieriger, wenn der Controller / Presenter direkt auf die Dialoge zugreift? Ich versuche sicherlich nicht zu streiten, verstehe nur weiter :)
trycatch

Ich denke, die wirkliche Kraft kommt aus zwei Richtungen: Die erste ist, dass die Ansicht und das Modell nichts mit anderen zu tun haben, und die zweite, dass der Großteil der Entwicklungsarbeit, die Engine der App, in einem ordentlich enthaltenen Prozess erledigt wird Einheit, der Controller. Aber eine gewisse Blutung der Verantwortung muss passieren. Zumindest der Großteil des Austauschs von Schnittstellen wird im Controller vorgenommen, und Verknüpfungen in der Ansicht sind minimal. Wie andere gesagt haben, ist ein gewisses Ausbluten der Logik zu erwarten und zulässig. MVC ist kein Wundermittel.
Philip Regan

Der wichtige Punkt für die Entkopplung ist, dass der Presenter NUR über gut definierte Schnittstellen (unabhängig von der UI-Bibliothek) auf die Ansicht zugreift. Auf diese Weise kann die UI-Bibliothek durch eine andere ersetzt werden (eine andere, die dieselbe Schnittstelle für Formular / Fenster / Dialog / Seite implementiert / control / whatever)
Marcel Toth

2

Im Allgemeinen soll Ihr Modell alle Interaktionen mit diesem Modell kapseln. Beispielsweise sind Ihre CRUD-Aktionen (Erstellen, Lesen, Aktualisieren, Löschen) Teil des Modells. Gleiches gilt für Sonderberechnungen. Dafür gibt es ein paar gute Gründe:

  • Es ist einfacher, das Testen für diesen Code zu automatisieren
  • So bleiben alle wichtigen Dinge an einem Ort

In Ihrem Controller (MVC-App) müssen Sie nur die Modelle sammeln, die Sie in Ihrer Ansicht verwenden müssen, und die entsprechenden Funktionen für das Modell aufrufen. Alle Änderungen am Modellstatus werden in dieser Ebene vorgenommen.

In Ihrer Ansicht werden nur die von Ihnen vorbereiteten Modelle angezeigt. Im Wesentlichen liest die Ansicht nur das Modell und passt die Ausgabe entsprechend an.

Abbildung des allgemeinen Prinzips auf tatsächliche Klassen

Denken Sie daran, dass Ihre Dialoge Ansichten sind. Wenn Sie bereits eine Dialogklasse haben, gibt es keinen Grund, eine weitere "View" -Klasse zu erstellen. Die Presenter-Ebene bindet das Modell im Wesentlichen an die Steuerelemente in der Ansicht. Die Geschäftslogik und alle wichtigen Daten sind im Modell gespeichert.

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.