Sollte die Ansicht im MVP-Muster ein Modellobjekt basierend auf dem Inhalt der Benutzeroberfläche instanziieren oder diese Inhalte einfach als Parameter an den Präsentator übergeben?


9

Ich verwende MVP-Muster in einer Android-App, die ich entwickle.

Ich habe grundsätzlich 4 Elemente:

  1. Die AddUserView, in der ein neuer Benutzer hinzugefügt werden kann:
  2. Der AddUserPresenter
  3. Die UserInfo (das Pojo)
  4. Der UserInfoManager (Businness-Logik und Speichermanager)

Meine Frage ist:

Wenn ich in der AddUserView auf die Schaltfläche "Hinzufügen" klicke, sollte der Inhalt der Textansichten abgerufen, eine neue UserInfo instanziiert und an den Presenter übergeben werden. Oder sollte die AddUserView nur die textViews-Inhalte abrufen und an den AddUserPresenter übergeben, wodurch die UserInfo instanziiert und an den UserInfoManager übergeben wird?

Antworten:


8

Laut Martin Fowlers Beschreibung von MVP ( http://martinfowler.com/eaaDev/uiArchs.html )

Über den View-Teil von MVC sagt Fowler:

Das erste Element von Potel besteht darin, die Ansicht als eine Struktur von Widgets zu behandeln, Widgets, die den Steuerelementen des Formular- und Steuerelementmodells entsprechen, und jegliche Trennung von Ansicht und Controller zu entfernen. Die Ansicht von MVP ist eine Struktur dieser Widgets. Es enthält kein Verhalten, das beschreibt, wie die Widgets auf Benutzerinteraktionen reagieren .

(Fettgedruckte Betonung meiner)

Dann vom Moderator:

Die aktive Reaktion auf Benutzerhandlungen lebt in einem separaten Präsentatorobjekt. Die grundlegenden Handler für Benutzergesten sind in den Widgets noch vorhanden, diese Handler geben jedoch lediglich die Kontrolle an den Präsentator weiter .

Der Moderator entscheidet dann, wie er auf das Ereignis reagieren soll. Potel diskutiert diese Interaktion hauptsächlich im Hinblick auf Aktionen am Modell, die durch ein System von Befehlen und Auswahlen ausgeführt werden. Hervorzuheben ist hier der Ansatz, alle Änderungen am Modell in einem Befehl zu verpacken. Dies bietet eine gute Grundlage für das Bereitstellen von Rückgängig- / Wiederherstellungsverhalten.

(Wieder kühne Betonung meiner)

In Übereinstimmung mit den Fowler-Richtlinien sollte Ihre Ansicht daher nicht für ein Verhalten als Reaktion auf das Schaltflächenereignis verantwortlich sein. Dazu gehört das Erstellen einer Instanz von UserInfo. Die Verantwortung für die Entscheidung, ein Objekt zu erstellen, liegt bei der Presenter-Methode, an die das UI-Ereignis weitergeleitet wird.

Man könnte jedoch auch argumentieren, dass der Schaltflächenereignishandler der Ansicht auch nicht für die Übergabe des Inhalts von Ihnen verantwortlich textViewsein sollte, da die Ansicht lediglich das Schaltflächenereignis an den Präsentator weiterleiten sollte und nicht mehr.

Bei MVP ist es üblich, dass die Ansicht eine Schnittstelle implementiert, über die der Präsentator Daten direkt aus der Ansicht abrufen kann (wobei sichergestellt wird, dass der Präsentator immer noch unabhängig von der Ansicht selbst ist). Da UserInfo ein einfaches POJO ist, kann es für die Ansicht gültig sein, einen Getter für UserInfo bereitzustellen, den der Präsentator über eine Schnittstelle aus der Ansicht abrufen kann.

// The view would implement IView
public interface IView {

    public UserInfo GetUserInfo();
}

// Presenter
public class AddUserPresenter {

    private IView addUserView;

    public void SetView(IView view) {
        addUserView = view
    }

    public void onSomethingClicked() {

        UserInfo userInfo = addUserView.GetUserInfo();
        // etc.
    }
}

Wie unterscheidet sich dies von der UserInfodirekten Übergabe des Ereignisses mit dem Ereignishandler an die Ansicht? Der Hauptunterschied besteht darin, dass der Präsentator letztendlich immer noch für die Logik verantwortlich ist, durch die ein UserInfoObjekt erstellt wird. dh das Ereignis erreichte den Präsentator vor der Erstellung des UserInfo, sodass der Präsentator die Entscheidung treffen konnte.

Stellen Sie sich ein Szenario vor, in dem Sie eine Präsentatorlogik hatten, in der Sie nicht wollten, dass UserInfodiese basierend auf einem bestimmten Status in der Ansicht erstellt wird. Zum Beispiel hat , wenn der Benutzer nicht eine Checkbox auf der Ansicht aktiviert, oder Sie haben eine Plausibilitätsprüfung gegen einig Feld in Userinfo hinzugefügt werden , die fehlgeschlagen - Ihr Moderator ist eine zusätzliche Kontrolle enthalten könnte vor dem Aufruf GetUserInfo- dh

    private boolean IsUsernameValid() {
        String username = addUserView.GetUsername();
        return (username != null && !username.isEmpty());
    }

    public void onSomethingClicked() {            

        if (IsUsernameValid()) {
            UserInfo userInfo = addUserView.GetUserInfo();
            // etc.
        }
    }

Diese Logik verbleibt im Präsentator und muss nicht zur Ansicht hinzugefügt werden. Wenn die Ansicht für das Aufrufen verantwortlich GetUserInfo()wäre, wäre sie auch für jede Logik verantwortlich, die ihre Verwendung umgibt. Das ist es, was das MVP-Muster zu vermeiden versucht.

Während die Methode, die das erstellt, UserInfomöglicherweise physisch in der View-Klasse vorhanden ist, wird sie niemals von der View-Klasse aufgerufen, sondern nur vom Presenter.

Wenn die Erstellung der UserInfoWidgets zusätzliche Überprüfungen des Inhalts von Benutzereingabe-Widgets erfordert (z. B. Zeichenfolgenkonvertierung, Validierung usw.), ist es natürlich besser, einzelne Getter für diese Dinge verfügbar zu machen, damit die Validierung / Zeichenfolgenkonvertierung durchgeführt werden kann Platzieren Sie innerhalb des Präsentators - und dann erstellt der Präsentator Ihre UserInfo.

Insgesamt besteht Ihr Hauptziel in Bezug auf die Trennung zwischen Präsentator und Ansicht darin, sicherzustellen, dass Sie niemals Logik in die Ansicht schreiben müssen. Wenn Sie ifaus irgendeinem Grund jemals eine Anweisung hinzufügen müssen (auch wenn es sich um eine ifAnweisung zum Status einer Widget-Eigenschaft handelt - Aktivieren eines leeren Textfelds oder eines Booleschen Werts für ein Kontrollkästchen), gehört diese in den Präsentator.


1
Tolle Antwort @BenCottrell! Aber ich habe noch eine andere :) Ist es eine gute Praxis, die Präsentationsmethoden onSomethingClicked()so zu benennen: Wenn der Benutzer auf "etwas" klickt, ruft die Ansicht auf presenter.onSomethingClicked()? Oder sollten meine Präsentationsmethoden in meinem Fall als die beabsichtigten Aktionen benannt werden addUser()?
Rômulo.Edu

1
@regmoraes Gute Frage; und ich denke, Sie haben in meinem Beispielcode einen leichten Geruch hervorgehoben . Das Presenterist natürlich eher für die UI-Logik als für die Domänenlogik verantwortlich und speziell auf die ViewKonzepte zugeschnitten . Daher sollten Konzepte, die existieren sollten, UI-Konzepte sein. Daher ist eine benannte Methode in der onSomethingClicked()Tat angemessen. Im Nachhinein riecht die Benennung, die ich in meinem obigen Beispiel gewählt habe, nicht ganz richtig :-).
Ben Cottrell

@ BenCottrell Zunächst vielen Dank für die tolle Antwort. Ich verstehe, dass es gültig ist, diese GetUserInfoMethode in der von Ihnen erwähnten Ansicht zu haben (wird vom Moderator ausgelöst). Was ist mit den möglichen ifBedingungen innerhalb der GetUserInfoMethode? Vielleicht werden einige Felder von UserInfo über die Benutzerreaktion festgelegt? Ein Szenario: Möglicherweise aktiviert der Benutzer ein Kontrollkästchen, dann sind einige neue Komponenten (möglicherweise ein neuer EditText) für den Benutzer sichtbar. In diesem Fall hat die GetUserInfoMethode eine if-Bedingung. In diesem Szenario GetUserInfoist noch gültig?
Blackkara

1
@Blackkara Betrachten Sie die Behandlung UserInfoals Modell der Ansicht (auch als " Ansichtsmodell " bezeichnet) - In diesem Szenario würde ich den booleanStatus des Kontrollkästchens und den leeren / nullbaren StringStatus des Textfelds hinzufügen UserInfo. Sie könnten sogar in Betracht ziehen, es umzubenennen, UserInfoViewModelwenn dies dazu beiträgt, dass das POJO eine Klasse ist, deren einziger wirklicher Zweck darin besteht UserInfoPresenter, Informationen über den Ansichtsstatus herauszufinden.
Ben Cottrell
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.