it-swarm-eu.dev

Implementierungsgedanken für Model-View-Presenter

Ich versuche, einen guten Überblick darüber zu bekommen, wie eine gute Entkopplung zwischen einer Benutzeroberfläche und dem Modell implementiert werden kann, aber ich habe Probleme, genau herauszufinden, wo die Linien geteilt werden sollen.

Ich habe mir Model-View-Presenter angesehen, bin mir aber nicht sicher, wie ich es implementieren soll. Zum Beispiel hat meine Ansicht mehrere Dialoge.

  • Sollte es eine View-Klasse mit Instanzen der einzelnen Dialoge geben? Wie sollten dann in diesem Fall die Dialoge mit dem Präsentator interagieren? dh. Wenn ein einzelner Dialog Daten über den Presenter vom Modell anfordern muss, wie sollte der Dialog einen Verweis auf den Presenter erhalten? Über einen Verweis auf die Ansicht, die ihm während des Baus gegeben wurde?
  • Ich dachte, vielleicht sollte die Ansicht eine statische Klasse sein? Dann die Dialoge GetView und holen Sie sich den Presenter von dort ...
  • Ich hatte darüber nachgedacht, den Presenter so einzurichten, dass er Eigentümer der Ansicht und des Modells ist (im Gegensatz zu der View mit dem Presenter und dem Presenter mit Model) und der Presenter Rückrufe für Ereignisse in der View registriert, aber das lässt es sehr viel erscheinen mehr gekoppelt (oder zumindest sprachabhängig.)

Ich versuche zu:

  1. mach das so entkoppelt wie möglich
  2. im Idealfall ist es möglich, den Präsentator/das Modell mit Ansichten anderer Sprachen zu koppeln (ich habe nicht viele sprachübergreifende Dinge getan, aber ich weiß, dass dies möglich ist, insbesondere, je mehr void(void) ich einhalten kann, mindestens eine C # -App mit einer C++ - Bibliothek ...
  3. halten Sie den Code sauber und einfach

Also .. irgendwelche Vorschläge, wie die Interaktionen gehandhabt werden sollen?

34
trycatch

Willkommen an einem rutschigen Hang. Zu diesem Zeitpunkt haben Sie erkannt, dass es eine endlose Variation aller Interaktionen zwischen Modell und Ansicht gibt. MVC, MVP (Taligent, Dolphin, Passive View), MVVM, um nur einige zu nennen.

Das Model View Presenter-Muster ist wie die meisten Architekturmuster für viel Abwechslung und Experimente offen. Allen Variationen ist gemeinsam, dass der Moderator als "Mittelsmann" zwischen Ansicht und Modell fungiert. Die beiden häufigsten sind die Passive View und die Supervising Presenter/Controller - [ Fowler ]. Passive Ansicht 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, die so viel Verantwortung an einen Präsentator delegiert. Supervising Presenter/Controller versucht, die in vielen UI-Frameworks integrierte Datenbindung zu nutzen. Die Benutzeroberfläche übernimmt die Datensynchronisation, aber der Präsentator/Controller sorgt für eine komplexere Logik. In beiden Fällen bilden Modell, Ansicht und Präsentator eine Triade

Es gibt viele Möglichkeiten, dies zu tun. Es ist sehr üblich, dies zu behandeln, indem jeder Dialog/jedes Formular als eine andere Ansicht behandelt wird. Oft besteht eine 1: 1-Beziehung zwischen Ansichten und Präsentatoren. Dies ist keine feste Regel. Es ist durchaus üblich, dass ein Moderator mehrere verwandte Ansichten bearbeitet oder umgekehrt. Es hängt alles von der Komplexität der Ansicht und der Komplexität der Geschäftslogik ab.

Wie Ansichten und Präsentatoren einen Bezug zueinander erhalten, wird manchmal als Verkabelung bezeichnet. Sie haben drei Möglichkeiten:

Die Ansicht enthält einen Verweis auf den Präsentator
Ein Formular oder Dialogfeld implementiert eine Ansicht. Das Formular verfügt über Ereignishandler, die über direkte Funktionsaufrufe an einen Präsentator weitergeleitet werden:

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

Da der Präsentator keinen Verweis auf die Ansicht hat, muss die Ansicht Daten als Argumente senden. Der Präsentator kann mithilfe von Ereignissen/Rückruffunktionen, auf die die Ansicht warten muss, zur Ansicht zurückkehren.

Der Moderator enthält einen Verweis auf die Ansicht
In diesem Szenario werden in der Ansicht Eigenschaften für die Daten angezeigt, die dem Benutzer angezeigt werden. Der Präsentator wartet auf Ereignisse und bearbeitet die Eigenschaften in 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 durch exponierte Eigenschaften.

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

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

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

37
Kenneth Cochran

Wie jeder gesagt hat, gibt es Dutzende von Meinungen und keine von ihnen ist richtig oder falsch. Ohne auf die Vielzahl von Mustern einzugehen und sich nur auf MVP zu konzentrieren, finden Sie 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, fügt sich in den Präsentator ein und zeigt die Methoden an, 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 Präsentator, aber in einigen Fällen können Sie viele Ansichten haben: einen Präsentator (Web, wpf usw.). Der Schlüssel hierbei ist, dass der Präsentator nichts von 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 Dialogfeld, eine Webseite usw. sein. Beachten Sie, dass der Konstruktor für die Ansicht den Präsentator erstellt, indem er sich selbst in ihn einfügt.

class View : IView
{
  private Presenter _presenter;

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

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

Dem Präsentator ist es egal, wie die Ansicht die Methode implementiert, die er gerade ausführt. Nach allem, was der Präsentator weiß, könnte es sein, dass er in eine Protokolldatei schreibt und diese dem Benutzer nicht einmal anzeigt.

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

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

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

Dies ist auch eine schlechte Mans-Implementierung, 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 Kontrollcontainern (IoC) wie Windsor, Ninject usw. untersuchen, die die Implementierung von IView für Sie bei Bedarf zur Laufzeit auflösen und es somit noch entkoppelter machen.

8
Bil Simser

Ich denke, es ist wichtig, sich daran zu erinnern, dass der Controller/Presenter der Ort ist, an dem die Aktion wirklich stattfindet. Eine Kopplung in der Steuerung ist aus Gründen der Notwendigkeit unvermeidlich.

Der Kernpunkt des Controllers besteht darin, dass sich das Modell nicht ändern muss, wenn Sie Änderungen an der Ansicht vornehmen, und mgekehrt (wenn sich das Modell ändert, muss sich die Ansicht auch nicht ändern) weil der Controller das Modell in die Ansicht und wieder zurück übersetzt. Der Controller ändert sich jedoch, wenn sich entweder das Modell oder die Ansicht ändert, 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 versetzen.

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 aus dem Modell gezogene Daten in ein string überträgt. im Debugger angezeigt werden (und durch Erweiterung in eine Nur-Text-Datei). Wenn ich Modelldaten aufnehmen und frei in Text übersetzen kann, ohne die Ansicht oder das Modell und nur den Controller zu ändern, bin ich auf dem richtigen Weg.

Davon abgesehen müssen Sie Referenzen zwischen den verschiedenen Komponenten haben, damit alles funktioniert. Der Controller muss über die View to Push-Daten Bescheid wissen. Die Ansicht muss über den Controller Bescheid wissen, 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 nichts anderes wissen sollte.

Vorbehalt : Ich komme aus einem Mac, Objective-C, Cocoa-Hintergrund, der Sie wirklich in das MVC-Paradigma treibt, ob Sie wollen oder nicht.

4
Philip Regan

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

  • Es ist einfacher, Ihre Tests für diesen Code zu automatisieren
  • Es hält all diese wichtigen Dinge an einem Ort

In Ihrem Controller (MVC-App) sammeln Sie lediglich die Modelle, die Sie in Ihrer Ansicht verwenden müssen, und rufen die entsprechenden Funktionen des Modells auf. Alle Änderungen am Modellstatus erfolgen in dieser Ebene.

In Ihrer Ansicht werden einfach die von Ihnen vorbereiteten Modelle angezeigt. Im Wesentlichen liest die Ansicht nur das Modell und passt seine 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 andere "Ansicht" -Klasse zu erstellen. Die Presenter-Ebene bindet das Modell im Wesentlichen an die Steuerelemente in der Ansicht. Die Geschäftslogik und alle wichtigen Daten werden im Modell gespeichert.

2
Berin Loritsch