我很高兴有人问了这个问题,因为 GWT desperatley 需要一种类似于 rails 的方式来构建应用程序。一种基于最佳实践的简单方法,适用于 90% 的所有用例,并实现超级容易的可测试性。
在过去的几年里,我一直在使用我自己的 MVP 实现,并以一种非常被动的观点来奴役 Presenter 告诉他做什么。
我的解决方案包括以下内容:
- 每个小部件的接口定义控制视觉外观的方法
- 一个实现类,可以是 Composite 或使用外部小部件库
- 一个屏幕的中央演示者,它托管由 M 个小部件组成的 N 个视图
- 每个屏幕的中心模型,保存与当前视觉外观相关的数据
- 通用侦听器类,例如“SourcesAddEvents[CustomerDTO]”(编辑器不喜欢这里的 java 泛型的真实符号,所以我使用了方括号),否则您将拥有许多相同的接口,只是类型不同
视图获取对演示者的引用作为其构造函数参数,因此它们可以使用演示者初始化它们的事件。演示者将处理这些事件并通知其他小部件/视图,或者调用 gwt-rpc 成功将其结果放入模型中。该模型有一个典型的“Property[List[String]] names = ....”属性更改侦听器机制,该机制已向演示者注册,以便通过 gwt-rpc 请求更新模型到所有视图/小部件感兴趣。
通过这种方法,我的 AsynInterfaces 使用 EasyMock 获得了非常容易的可测试性。我还能够轻松地交换视图/小部件的实现,因为我所需要重写的只是通知演示者某些事件的代码 - 无论底层小部件(按钮、链接等)如何。
我的方法存在的问题:
- 我当前的实现很难在不同屏幕的中心模型之间同步数据值。假设您有一个显示一组类别的屏幕和另一个允许您添加/编辑这些项目的屏幕。目前很难将这些更改事件传播到屏幕的边界,因为这些值被缓存在这些模型中,并且很难找到我们是否有些东西是脏的(在传统的 web1.0-html 中很容易-dumb-terminal 一种带有服务器端声明式缓存的场景)。
- 视图的构造函数参数可以实现超级简单的测试,但是如果没有可靠的依赖注入框架,“onModuleLoad()”中就会有一些 UGLY 工厂/设置代码。在我开始这个的时候,我不知道谷歌 GIN,所以当我重构我的应用程序时,我会用它来摆脱这个样板。一个有趣的例子是 GIN-Trunk 中的“HigherLower”游戏。
- 我第一次没有得到正确的历史记录,所以很难从我的应用程序的一个部分导航到另一个部分。我的方法没有意识到历史,这是一个严重的衰退。
我对这些问题的解决方案:
- 使用 GIN 删除难以维护的设置样板
- 从 Gwt-Ext 迁移到 GXT 时,使用其 MVC 框架作为 EventBus 来附加/分离模块化屏幕,以避免缓存/同步问题
- 想一想 Ray Ryan 在 I/O 09 上的演讲中描述的某种“地点”-抽象,它弥合了 GXT-MVC 和 GWTs-Hitory 方法之间的 Event-Gap
- 为小部件使用 MVP 来隔离数据访问
概括:
我不认为可以为整个应用程序使用单一的“MVP”方法。应用程序导航肯定需要历史记录,需要像 GXT-MVC 这样的事件总线来附加/分离屏幕,以及 MVP 来轻松测试小部件的数据访问。
因此,我提出了一种结合这三个元素的分层方法,因为我相信“one-event-mvp-system”解决方案不会起作用。导航/屏幕附加/数据访问是三个独立的关注点,我将在接下来的几个月中重构我的应用程序(迁移到 GXT),以分别针对每个关注点使用所有三个事件框架(该工作的最佳工具)。所有三个元素不需要相互了解。我知道我的解决方案只适用于 GXT 项目。
在编写大型 GWT 应用程序时,我觉得我必须在客户端重新发明类似 Spring-MVC 的东西,这真的很糟糕,因为要吐出像 Spring MVC 这样优雅的东西需要大量的时间和脑力。GWT 需要一个应用程序框架,而不是那些编译器人员努力工作的微小的 JS 优化。