【问题标题】:MVP Pattern in GWT: How to initialize Model/View and handle server requests in the Presenter?GWT 中的 MVP 模式:如何在 Presenter 中初始化模型/视图并处理服务器请求?
【发布时间】:2015-08-21 17:26:49
【问题描述】:

这个问题不一定是 GWT 有界的,但另一方面,这正是我尝试正确使用它的原因。

我在 GWT 中使用 MVP 模式(推荐),但我不确定如何处理模型和视图,也不确定如何创建对服务器。特别想知道

  • 在哪里初始化MyView?演示者是在构造函数中自己执行此操作,还是接收到有效引用作为构造函数的参数?
  • 与MyModel 相同。由于MyModel 的实例可能在不同的演示者之间共享,我想它应该传递给演示者的构造函数。
  • 如何在 Presenter 中向服务器发出请求?我是否有一个额外的层来处理我的请求,或者是否建议在那里也使用MyServletAsync?

我很难找出一个好的设计。请参阅下面的示例代码,该示例代码显示了我现在的操作方式:

public class MyPresenter implements MyView.MyPresenter

    private final MyServletAsync myService = MyServlet.Util.getInstance();

    private MyModel myModel;
    private MyView myView;

    public MyPresenter(MyView myView, MyModel myModel) {

        this.myView = myView;
        this.myModel = myModel;

        // Register click handler ..

        this.myView.getNextXyzButton.addClickHandler(
            new ClickHandler() {
                @Override
                public onClick(Event event) {
                    requestXyz(this.myModel.getXyz().getOffset() + 1);
                }
            });

        this.myView.getPrevisousXyzButton.addClickHandler(
            new ClickHandler() {
                @Override
                public onClick(Event event) {
                    requestXyz(this.myModel.getXyz().getOffset() - 1);
                }
            });

        // Initialize Xyz with offset 0
        requestXyz(0);
    }

    /*
     * Should I do this here in the presenter or should I 
     * create another layer that handles requests?
     */
    private void requestXyz(final int byOffset) {
        myService.getXyzFromServer(byOffset, new AsyncCallback<XyzDto>() {
            @Override
            public void onSuccess(XyzDto result) {
                updateModelWithResult(result);
            }
            @Override
            public void onFailure(Throwable caught) {
                displayError(caught);
            }
        });
    }

    private updateModelWithResult(Xyz result) {     
        this.myModel.setXyz(result);
        this.myView.displayXyz(result);
    }

    private displayError(Throwable caught) {
        // ..
    }

}

感谢您的建议。

【问题讨论】:

标签: java gwt mvp


【解决方案1】:

MVP 模式很棒,它旨在隔离但调解完全不同的事物。但是,该模式并没有规定它是如何实现的,这完全取决于您自己,但肯定会尊重某些方面。根据我的经验,我想说:

在哪里初始化 MyView?

Presenter 最好接受某种视图工厂(或一般的供应商,因为我们已经可以持有现有视图,尤其是在 GWT 情况下),并让 Presenter 决定何时实例化或接受视图。我当前的项目使用了一个小型自定义库,它定义了一个抽象演示者,这是它的实现方式:

public abstract class AbstractPresenter<M extends IModel, V extends IView>
        implements IPresenter<M, V> {

    private final M model;
    private final V view;

    protected AbstractPresenter(final M model, final IViewFactory<V, ? super IPresenter<M, V>> viewFactory) {
        this.model = model;
        view = viewFactory.createView(this);
    }

...

}

传递视图工厂的主要原因是将演示者注入视图,因为视图大多应该引用它们各自的演示者(+我更喜欢有最终字段,所以这是一种同时拥有最终引用的方法:演示者到查看,并查看给演示者)。

我猜应该是传递给presenters的构造函数。

是的,它可以像上面的例子一样通过构造函数传递,但是有些人可能更喜欢 setModel 和 setView 这样的集合访问器。

如何在 Presenter 中向服务器发出请求?

这就是模型的用途。 Presenter 只是模型和视图之间的中介,它基本上只负责双向的用户交互。考虑您的模型是访问应用程序的一种方式,因为模型不应被视为虚拟的“获取/设置字段”对象。模型是对服务层、网络通信(可以是服务背后的抽象)、数据存储或任何应用程序的抽象。因此,您的演示者只需通知模型获取或放置一些数据(分别从/到其他地方)。这也允许您编写完全抽象的层。如果您进行单元测试,那么由于private final MyServletAsync myService = MyServlet.Util.getInstance(); 需要启动 servlet(这是个坏主意,对吗?),您在测试演示者时遇到了麻烦。因此,如果getXyzFromServer 成为模型的一部分(我会说,它可能被命名为getXyz,因为演示者并不真正关心Xyz 是从哪里获取的),您可以轻松地模拟您的模型并进行测试纯粹的演示者。

【讨论】:

  • 嗨!谢谢你的回答,我知道了它的主要部分,但我仍然想知道在模型中如何处理异步调用。如果例如演示者告诉模型对其数据执行更新,那么我将如何通知视图进行自我更新?我要么需要在模型中引用演示者,要么需要在数据到达时为演示者回调 - 老实说,我认为在演示者中处理这些请求然后相应地更新模型和视图会更容易(也许在演示者前面添加一个额外的服务层)。
  • 再一次,考虑你的模型是一扇通向外部世界的大门,所以演示者只会更新与业务逻辑无关的视图(假设模型从某个地方接收数据并自行更新)。我会选择在模型后面设置服务层。让演示者只知道两个对象:模型和视图;并让您的模型没有对演示者的引用(例如M-&gt;[]、P-&gt;[M, V]、V-&gt;[P])。这大大简化了事情。你的模型可以提供接受回调的异步方法,一旦你的演示者内回调被调用,你的演示者只更新视图。
  • 看来我的问题是直到现在我认为模型应该基本上只保存数据并且只为我的 DAO 对象提供 getter/setter 方法。我正在考虑将我的 DTO 传递给一个服务层,该服务层将有效的 DAO 对象返回给演示者,该演示者设置/更新我的模型中的那些对象,并告诉视图也进行自我更新。该模型无法为其拥有的所有数据提供功能。
  • 是的,你做对了。网络通信是一种选择。你可以在你的模型中做任何你想做的事情。简单地说,M 是从 P 的角度看应用程序业务逻辑的样子。在你的问题代码中,据我了解,MyServletAsync 比MyModel 更像是一个模型。它应该只是一个获取/设置驱动的数据持有者,对吧?如果是这样,那么拥有像MyModel 这样的虚拟对象就没有实际意义。只是让它不是一个虚拟的数据持有者,而是将MyServletAsync 封装到其中,并声明您的模型接口。就是这样
  • “我可能不想在另一个视图中重新使用整个 M,但可能是另一个模型中的一个 DAO 对象。这有意义吗?” - 是的,确实如此,这就是想法。我个人不喜欢重复使用模型,因为它们大多不适合任何其他演示者。同样,假设您的模型就像让您的演示者通过 MVP 上下文中的接口访问 something 只是为了更新/与视图交互,因此它不需要了解模型内部,因此您的模型可以以任何方式实现(具体对象、模拟等)。
猜你喜欢
  • 2019-11-26
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
相关资源
最近更新 更多