Nel modello MVP, la visualizzazione deve creare un'istanza di un oggetto Modello in base al contenuto dell'interfaccia utente o semplicemente passare questi contenuti come parametri al Presenter?


9

Sto usando il modello MVP in un'app Android che sto sviluppando.

Ho fondamentalmente 4 elementi:

  1. AddUserView in cui è possibile aggiungere un nuovo utente:
  2. AddUserPresenter
  3. The UserInfo (il pojo)
  4. UserInfoManager (logica di gestione e gestore archiviazione)

La mia domanda è:

Quando premo il pulsante "Aggiungi" in AddUserView, dovrebbe ottenere il contenuto delle visualizzazioni di testo, creare un'istanza di un nuovo UserInfo e passarlo al Presenter. O AddUserView dovrebbe semplicemente ottenere i contenuti textViews e passarli a AddUserPresenter, che di fatto crea un'istanza di UserInfo e lo passa a UserInfoManager?

Risposte:


8

Secondo la descrizione di MVP di Martin Fowler ( http://martinfowler.com/eaaDev/uiArchs.html )

Della parte View di MVC, Fowler afferma:

Il primo elemento di Potel è considerare la vista come una struttura di widget, widget che corrispondono ai controlli del modello Forms and Controls e rimuovere qualsiasi separazione vista / controller. La vista di MVP è una struttura di questi widget. Non contiene alcun comportamento che descriva come i widget reagiscono all'interazione dell'utente .

(Enfasi in grassetto mia)

Quindi del presentatore:

La reazione attiva all'utente agisce in un oggetto presentatore separato. I gestori fondamentali per i gesti degli utenti esistono ancora nei widget, ma questi gestori passano semplicemente il controllo al presentatore .

Il relatore decide quindi come reagire all'evento. Potel discute questa interazione principalmente in termini di azioni sul modello, cosa che fa tramite un sistema di comandi e selezioni. Una cosa utile da evidenziare qui è l'approccio di impacchettare tutte le modifiche al modello in un comando: ciò fornisce una buona base per fornire il comportamento di annullamento / ripetizione.

(Ancora una volta, il mio enfasi è in grassetto)

Pertanto, in conformità con le linee guida di Fowler, la tua vista non dovrebbe essere responsabile di alcun comportamento in risposta all'evento pulsante; che include la creazione di un'istanza di UserInfo. La responsabilità di decidere di creare un oggetto appartiene al metodo Presenter al quale viene inoltrato l'evento UI.

Tuttavia, si potrebbe anche sostenere che anche il gestore di eventi del pulsante della vista non dovrebbe essere responsabile del passaggio del contenuto del tuo textView, poiché la vista dovrebbe semplicemente inoltrare l'evento del pulsante nel Presentatore e nient'altro.

Con MVP, è comune per la vista implementare un'interfaccia che il relatore può utilizzare per raccogliere i dati direttamente dalla vista (assicurando nel contempo che il presentatore sia ancora agnostico alla vista stessa). Poiché UserInfo è un semplice POJO, potrebbe essere valido per la vista esporre un getter per UserInfo che il Presentatore può prelevare dalla Vista tramite un'interfaccia.

// 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.
    }
}

In che modo questo differisce dal passare UserInfodirettamente nella vista usando il gestore eventi? La differenza principale è che il relatore è ancora alla fine responsabile della logica che causa la UserInfocreazione di un oggetto. cioè l'evento ha raggiunto il Presentatore prima della creazione del UserInfo, consentendo al Presentatore di prendere la decisione.

Immagina uno scenario in cui avevi la logica del presentatore in cui non volevi che UserInfofosse creato in base a uno stato all'interno della vista. Ad esempio, se l'utente non ha spuntato una casella di spunta sulla vista, o hai avuto un controllo di validazione su un campo da aggiungere a UserInfo che non è riuscito - il tuo presentatore potrebbe contenere un controllo aggiuntivo prima di chiamare GetUserInfo- ad es.

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

    public void onSomethingClicked() {            

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

Tale logica rimane all'interno del presentatore e non è necessario aggiungerla alla vista. Se la vista fosse responsabile della chiamata GetUserInfo(), sarebbe anche responsabile di qualsiasi logica che lo circonda; che è ciò che il modello MVP sta cercando di evitare.

Quindi, mentre il metodo che crea che UserInfopuò esistere fisicamente nella classe View, non viene mai chiamato dalla classe View, solo dal Presenter.

Ovviamente, se la creazione dei UserInforisultati richiede controlli aggiuntivi rispetto al contenuto dei widget di input dell'utente (ad es. Conversione di stringhe, convalida, ecc.), Sarebbe meglio esporre singoli getter per quelle cose in modo che la convalida / conversione di stringhe possa richiedere posto all'interno del Presentatore - e quindi il relatore crea il tuo UserInfo.

Nel complesso, il tuo obiettivo principale per quanto riguarda la separazione tra Presenter / View è assicurarti di non dover mai scrivere la logica nella vista. Se ti capita mai di dover aggiungere ifun'istruzione per qualsiasi motivo (anche se si tratta di ifun'istruzione relativa allo stato di una proprietà del widget - controllo di una casella di testo vuota o di un valore booleano per una casella di controllo), allora appartiene al presentatore.


1
Ottima risposta @BenCottrell! Ma ne ho un altro :) È buona norma nominare i metodi del presentatore come onSomethingClicked(), quindi quando l'utente fa clic su "qualcosa", il View chiama presenter.onSomethingClicked()? O i miei metodi presentatore dovrebbero essere nominati come le azioni previste, nel mio caso addUser()?
Rômulo.Edu,

1
@regmoraes Buona domanda; e penso che tu abbia evidenziato un leggero odore nel mio codice di esempio. Il Presenterè ovviamente responsabile logica UI piuttosto che la logica del dominio, e specificamente alle Viewpertanto concetti che devono intercorrere sono concetti UI, quindi un metodo chiamato onSomethingClicked()è infatti opportuno. Con il senno di poi, la denominazione che ho scelto nel mio esempio sopra non ha un buon odore :-).
Ben Cottrell,

@BenCottrell Innanzitutto molte grazie per l'ottima risposta. Capisco, è valido avere questo GetUserInfometodo nella vista come hai menzionato (verrà attivato dal presentatore) Che dire delle possibili ifcondizioni all'interno del GetUserInfometodo? Forse alcuni campi di UserInfo verranno impostati tramite la reazione dell'utente? Uno scenario: forse l'utente seleziona una casella di controllo, quindi alcuni nuovi componenti (forse un nuovo EditText) saranno visibili all'utente. Quindi, in tal caso, il GetUserInfometodo avrà if condition. In questo scenario GetUserInfoè valido ancora?
Blackkara,

1
@Blackkara Considerare il trattamento UserInfocome un modello di vista (aka "Visualizza modello") - In quello scenario aggiungerei lo booleanstato della casella di controllo e lo stato vuoto / nullable Stringdella casella di testo UserInfo. Potresti anche considerare di rinominarlo UserInfoViewModelse questo aiuta a pensare in termini di POJO come una classe il cui unico vero scopo è quello di far UserInfoPresenterscoprire informazioni sullo stato View.
Ben Cottrell,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.