在MVP模式中,View应该基于UI内容实例化Model对象,还是仅将这些内容作为参数传递给Presenter?


9

我正在开发的Android应用程序中使用MVP模式。

我基本上有4个元素:

  1. 可以在其中添加新用户的AddUserView:
  2. AddUserPresenter
  3. UserInfo(pojo)
  4. UserInfoManager(业务逻辑和存储管理器)

我的问题是:

当我在AddUserView中按下“添加”按钮时,它应该获取文本视图的内容,实例化一个新的UserInfo并将其传递给Presenter。还是AddUserView应该只获取textViews内容并将其传递给AddUserPresenter,而后者实际上将实例化UserInfo并将其传递给UserInfoManager?

Answers:


8

根据Martin Fowler对MVP的描述(http://martinfowler.com/eaaDev/uiArchs.html

在MVC的View部分中,Fowler说:

Potel的第一个元素是将视图视为窗口小部件的结构,这些窗口小部件与Forms and Controls模型的控件相对应,并删除任何视图/控件分离。MVP的视图是这些小部件的结构。它不包含任何描述小部件如何对用户交互做出反应的行为

(加粗强调我的)

然后,演示者:

对用户行为的积极反应生活在单独的演示者对象中。小部件中仍然存在用户手势的基本处理程序,但是这些处理程序仅将控制权传递给presenter

然后,演示者决定如何对事件做出反应。Potel主要根据模型上的动作来讨论这种交互,它是通过命令和选择系统来完成的。这里要强调的一个有用的事情是将所有编辑内容打包到命令中的方法-这为提供撤消/重做行为提供了良好的基础。

(再次,加粗强调我的)

因此,按照Fowler的指南,您的View不应对响应按钮事件的任何行为负责;其中包括创建的实例UserInfo。决定创建对象的责任属于将UI事件转发到的Presenter方法。

但是,也可能会争辩说View的按钮事件处理程序也不应该负责传递两者的内容textView,因为View应该只是将按钮事件转发到Presenter中,而仅此而已。

使用MVP,视图通常会实现一个接口,演示者可以使用该接口直接从视图中拾取数据(同时确保演示者仍然对视图本身不了解)。由于UserInfo是简单的POJO,因此对于视图公开UserInfo 的getter可能是有效的,Presenter可以通过接口从该Viewer拾取它。

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

UserInfo与使用事件处理程序将直接传递到视图有何不同?主要区别在于,演示者仍然最终负责导致创建对象的逻辑UserInfo。也就是说,事件在创建之前已到达Presenter UserInfo,从而使Presenter可以做出决定。

设想一个场景,您有演示者逻辑,您不想UserInfo基于视图中的某些状态创建演示者逻辑。例如,如果用户没有在视图上打勾,或者您对要添加到UserInfo中的某个字段进行了验证检查,但失败了-您的演示者可能在调用之前包含其他检查GetUserInfo-即

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

    public void onSomethingClicked() {            

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

该逻辑保留在演示者内部,无需将其添加到视图中。如果视图负责调用,GetUserInfo()那么它也将负责与其使用有关的任何逻辑。这就是MVP模式要避免的事情。

因此,尽管创建方法实际上UserInfo可能存在于View类中,但从视图类中永远不会调用它,而只能从Presenter中调用它。

当然,如果UserInfo最终创建过程需要对用户输入窗口小部件的内容进行其他检查(例如,字符串转换,验证等),那么最好公开那些单独的吸气剂,以便进行验证/字符串转换放置在主持人内-然后主持人创建您的广告UserInfo

总体而言,关于Presenter / View之间的分离的主要目标是确保您永远不需要在视图中编写逻辑。如果您发现if由于任何原因需要添加一条语句(即使它是if有关小部件属性状态的一条语句-检查一个空的文本框或一个复选框的布尔值),那么它就属于演示者。


1
好答案@BenCottrell!但是我还有另一个:)是将presenter方法命名为的一种好习惯onSomethingClicked(),因此,当用户单击“某物”时,View调用presenter.onSomethingClicked()吗?或者在我的情况下,我的演示者方法应该命名为预期的操作addUser()
Rômulo.Edu

1
@regmoraes好问题;而且我认为您在示例代码中已经强调了一点气味。的Presenter当然是负责UI逻辑而不是域逻辑的,并且具体地修整View,因此必须存在概念是UI的概念,所以命名方法onSomethingClicked()确实是合适的。事后看来,我在上面的示例中选择的命名并不十分正确:-)。
Ben Cottrell

@BenCottrell首先,非常感谢您的出色回答。我了解,GetUserInfo在您提到的视图中使用此方法是有效的(将由演示者触发)方法if内部的可能条件GetUserInfo如何?也许通过用户反应来设置UserInfo的某些字段?一个场景:也许用户选中一个复选框,那么用户将可以看到一些新组件(也许是一个新的EditText)。因此,在这种情况下,GetUserInfo方法将具有if条件。在这种情况下GetUserInfo仍然有效吗?
blackkara '17

1
@Blackkara考虑治疗UserInfo作为模型的最视图(又名“视图模型”) -在这种情况下我想补充的boolean复选框和空/可为空的状态String的文本框进入状态UserInfo。您甚至可以考虑将其重命名为,UserInfoViewModel如果这样有助于考虑POJO是一个类,其真正目的只是为了UserInfoPresenter找出有关View状态的信息。
本·科特雷尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.