2017-09-01 93 views
1

在MVP模式中,视图和模型之间不存在依赖关系。一切都由主持人完成。实现MVP的最佳方法

在我们的GWT项目中,我们实现了所有我们的MVP类,如GWT .page所示。要访问视图,我们的主持人使用的界面是这样的:

public interface Display { 
    HasClickHandlers getAddButton(); 
    HasClickHandlers getDeleteButton(); 
    ... 
} 

所以,主持人会从视图按钮,并应用到其点击处理它。

但这种感觉对我来说太错了。这违反了单一责任原则以及信息隐藏原则。主持人不应该了解视图内部的任何内容(即使我们得到抽象的HasClickHandlers)。

在我看来,我们应该更好地使用这种模式:

public interface Display { 
    void setPresenter(Presenter p); 
} 

public interface Presenter { 
    void onAdd(); 
    void onDelete(); 
} 

因此,有观点告诉主持人积极,有些互动发生。但不是在哪个视图元素上。 我的团队合作伙伴认为,在第一种解决方案中,我们避免了循环依赖。那就对了。但无论如何,我宁愿采用第二种方式,因为这些模块可以更好地分离,并且可以独立维护。

有什么优点/缺点?

+0

你的问题似乎是StackOverflow的主题,因为它主要是基于意见的。您可能想查看[代码审查](https://codereview.stackexchange.com/help/on-topic),但请确保您的问题首先适合您。 – Baz

+0

我想知道给我一个更好的感觉,使用哪个优点/缺点。 –

+0

是的,我明白,但这样的问题在这里是无关紧要的。请阅读[帮助页](https://stackoverflow.com/help/on-topic)关于题目的问题类型:“太宽泛,不清楚,不完整或主要基于意见的问题* *可能会被社区搁置,直到他们得到改善。“ – Baz

回答

3

非常同意 - 如果您继续阅读第二部分,则链接的文章将描述双方。

我不建自己的看法暴露部件或他们的HasSomethingHandlers - 我觉得它通常更好地建设演示API暴露出以下几个原因这些可能的相互作用:

  • 单元测试 - 这是简单做一个模拟视图,调用方法而不是触发事件。这并不是说这是不可能的(特别是如果您的Presenter可以在JVM上运行并且您可以为每个JVM创建代理类型)

  • 清理未使用的处理程序 - 演示者通常很便宜,每次都会创建,但有时候视图会很重并且会被保存,重用,任何时候重用视图时,都必须确保所有这些外部添加的处理程序都被移除了,这可以通过一些“生命周期”方法正确完成,如onStop ()或主持人的东西,所以无论代码替代演示者可以关闭它,但需要考虑因素。

  • 多视图实现 - 像单元测试,不同的视图实现(移动与桌面,只读vs读写等)可能有不同的方式导致相同的变化。现在,您可以使用多种方法公开HasClickHandlers onAddClicked()HasTouchHandlers onAddTapped(),或采取您描述的方法。 (您也可能会泄露一些UX细节到演示者中,例如在最后一个字段中敲击Enter键,而不是单击该按钮 - 可以认为属于视图中,而不是演示者)。

这种方法的缺点之一是:视图需要更多的大脑,而MVP的部分目标是保持视图尽可能瘦。

最后,虽然这种替代方法是不实际比较和对比在页面上你联系,你链接的同一篇文章的第二页确实显示出你的方法,而不是http://www.gwtproject.org/articles/mvp-architecture-2.html

现在我们有了用户界面我们需要连接关联的UI事件,即添加/删除按钮点击以及与联系人列表FlexTable的交互。这是我们开始注意到应用程序设计总体布局的重大变化的地方。主要是因为我们想通过UiHandler注释将视图内的方法链接到UI交互。第一个主要变化是我们希望我们的ContactsPresenter实现一个Presenter接口,该接口允许我们的ContactsView在接收到点击,选择或其他事件时回调给主持人。该演示接口定义如下:

public interface Presenter<T> { 
    void onAddButtonClicked(); 
    void onDeleteButtonClicked(); 
    void onItemClicked(T clickedItem); 
    void onItemSelected(T selectedItem); 
} 

这是在描述建筑享有UiBinder的方式的背景下完成的,但它也同样适用,而不UiBinder的。

因此,在讨论与组织团队如何构建应用程序相关的文章时,请务必考虑整套文章 - 这些文章都参考http://www.gwtproject.org/doc/latest/DevGuideMvpActivitiesAndPlaces.html,这些文章也采用了这种非事件处理方法。在那篇文章中,还有其他“错误”的做法,比如手动依赖注入,而不是像Gin或Dagger2这样的自动化可靠工具。

这些文章并不是要描述一种真实的方式,而是要了解可以使用的想法和模式。不要对货物进行崇拜,盲目地应用模式,但要确保理解利弊 - 并且作为一个团队工作,不一致的模式可能比没有模式更糟糕!


编辑:我意识到我没有提到循环依赖问题!

这就像你想要的一样多或少的问题。如果你同时创建它们,并且在构造函数中传递对方的引用,那么显然你有一个问题 - GWT无法对它们进行代理(无论如何,这可说是一个坏主意)。但是,随着时间的推移,您可能会陷入一种情况,即构建视图的开销很大,并且可能会被集中/缓存,并且不会每次都重新创建,在这种情况下,需要向视图通知它现在可以工作的新主持人。

然后要求View界面支持一个void setPresenter(P)方法,所以我们的圆圈被打破了。请记住,这种方法要求演示者必须清除视图中的数据,或者需要知道何时新演示者设置为清除其自己的数据的视图。

我个人对此的方法导致主持人拥有一个视图字段,在创建时注入(可能通过构造函数),并且在演示者中有一个start方法。当被调用时,演示者控制视图,准备就绪后,将其附加到dom层次结构所属的位置。

是的,这需要一个额外的方法在视图中,但如果你有任何基类为您的意见,这将是微不足道的,如果你最终做任何视图重用,无论如何,你需要该方法。