有没有build议的方式来使用MVP使用GWT Observer模式?

我正在考虑根据使用GWT的MVP模式来实现一个用户界面,但是对如何进行操作有疑问。

这些是(一些)我的目标:

  • 主持人对UI技术一无所知(即不使用com.google。*)
  • 该视图对主持人一无所知(不确定,但如果我想它是模型不可知的,但)
  • 模型对视图或主持人一无所知(显然)

我将在视图和演示者之间放置一个接口,并使用Observer模式将这两个视图分离开来:视图生成事件并通知演示者。

令我困惑的是GWT中不支持java.util.Observer和java.util.Observable。 这表明,我所做的并不是推荐的方法,就GWT而言,这引起了我的问题:使用GWT实现MVP的推荐方法,特别是考虑到上述目标,推荐的方法是什么? 你会怎么做?

程序结构

我就是这么做的 Eventbus允许演示者(扩展抽象类Subscriber )订阅属于我应用中不同模块的事件。 每个模块对应于我系统中的一个组件,每个模块都有一个事件types,一个演示者,一个处理程序,一个视图和一个模型。

订阅所有types为CONSOLE的事件的发言人将接收从该模块触发的所有事件。 对于更细粒度的方法,你总是可以让演示者订阅特定的事件,比如NewLineAddedEvent或类似的东西,但是对于我来说,我发现在模块级别处理它足够好。

如果你愿意,你可以打电话给主持人的救援方法asynchronous,但到目前为止,我发现自己几乎没有必要这样做。 我想这取决于你确切的需求是什么。 这是我的EventBus

 public class EventBus implements EventHandler { private final static EventBus INSTANCE = new EventBus(); private HashMap<Module, ArrayList<Subscriber>> subscribers; private EventBus() { subscribers = new HashMap<Module, ArrayList<Subscriber>>(); } public static EventBus get() { return INSTANCE; } public void fire(ScEvent event) { if (subscribers.containsKey(event.getKey())) for (Subscriber s : subscribers.get(event.getKey())) s.rescue(event); } public void subscribe(Subscriber subscriber, Module[] keys) { for (Module m : keys) subscribe(subscriber, m); } public void subscribe(Subscriber subscriber, Module key) { if (subscribers.containsKey(key)) subscribers.get(key).add(subscriber); else { ArrayList<Subscriber> subs = new ArrayList<Subscriber>(); subs.add(subscriber); subscribers.put(key, subs); } } public void unsubscribe(Subscriber subscriber, Module key) { if (subscribers.containsKey(key)) subscribers.get(key).remove(subscriber); } } 

处理程序连接到组件,并负责将本地GWT事件转换为专门用于我的系统的事件。 下面的处理程序简单地通过将它们包装在一个自定义的事件中,然后将它们发送到EventBus以供订阅者处理。 在某些情况下,处理程序在开始事件之前执行额外的检查是有意义的,或者有时甚至在决定天气之前还是不发送事件。 处理程序添加到graphics组件时,会在处理程序中执行该操作。

 public class AppHandler extends ScHandler { public AppHandler(Action action) { super(action); } @Override public void onClick(ClickEvent event) { EventBus.get().fire(new AppEvent(action)); } 

Action是一个枚举,expression了我的系统中可能的数据操作方式。 每个事件都通过一个Action初始化。 演示者使用该动作来确定如何更新其视图。 带有ADD操作的事件可能会使演示者向菜单添加一个新button,或向网格添加一个新行。

 public enum Action { ADD, REMOVE, OPEN, CLOSE, SAVE, DISPLAY, UPDATE } 

被处理程序触发的事件看起来有点像这样。 注意事件如何为它的消费者定义一个接口,这将确保你不要忘记实现正确的救援方法。

 public class AppEvent extends ScEvent { public interface AppEventConsumer { void rescue(AppEvent e); } private static final Module KEY = Module.APP; private Action action; public AppEvent(Action action) { this.action = action; } 

主持人订阅属于不同模块的事件,然后在他们被解雇时解救他们。 我还让每个演示者为其视图定义一个接口,这意味着演示者将不必知道任何关于实际graphcal组件的信息。

 public class AppPresenter extends Subscriber implements AppEventConsumer, ConsoleEventConsumer { public interface Display { public void openDrawer(String text); public void closeDrawer(); } private Display display; public AppPresenter(Display display) { this.display = display; EventBus.get().subscribe(this, new Module[]{Module.APP, Module.CONSOLE}); } @Override public void rescue(ScEvent e) { if (e instanceof AppEvent) rescue((AppEvent) e); else if (e instanceof ConsoleEvent) rescue((ConsoleEvent) e); } } 

每个视图都有一个HandlerFactory的实例,负责为每个视图创build正确types的处理程序。 每个工厂都使用一个Module实例化,用于创build正确types的处理程序。

 public ScHandler create(Action action) { switch (module) { case CONSOLE : return new ConsoleHandler(action); 

该视图现在可以自由地为其组件添加不同types的处理程序,而无需知道具体的实现细节。 在这个例子中,所有的视图需要知道的是, addButtonbutton应该被链接到一些对应于动作ADD的行为。 这种行为将由获得该事件的主持人决定。

 public class AppView implements Display public AppView(HandlerFactory factory) { ToolStripButton addButton = new ToolStripButton(); addButton.addClickHandler(factory.create(Action.ADD)); /* More interfacy stuff */ } public void openDrawer(String text) { /*Some implementation*/ } public void closeDrawer() { /*Some implementation*/ } 

考虑一个简化的Eclipse,其中左边是类层次结构,右边是代码的文本区域,顶部是菜单栏。 这三种不同的观点与三个不同的主持人,因此他们会构成三个不同的模块。 现在,文本区域将需要根据类别层次结构中的变化而改变是完全可能的,因此文本区域呈现者不仅要订阅从文本区域内发起的事件,还要订阅事件被从class级层级解雇。 我可以想像这样的事情(每个模块都会有一组类 – 一个处理程序,一个事件types,一个演示者,一个模型和一个视图):

 public enum Module { MENU, TEXT_AREA, CLASS_HIERARCHY } 

现在考虑从层次结构视图中删除类文件后,我们希望我们的视图能够正确更新。 这应该会导致对gui的以下更改:

  1. 类文件应该从类层次结构中删除
  2. 如果类文件被打开,并因此在文本区域中可见,则应该closures它。

两个演示者,一个控制树视图和一个控制文本视图的人,都会订阅从CLASS_HIERARCHY模块触发的事件。 如果事件的行为是“ REMOVE ,那么两个预先员都可以采取适当的行动,如上所述。 控制层次结构的演示者大概也会向服务器发送消息,确保删除的文件实际上被删除了。 这个设置允许模块通过监听事件总线发出的事件来响应其他模块中的事件。 联系很less,交换意见,主持人或处理程序是完全无痛的。

为了我们的项目,我在这些方面取得了成就。 我想要一个事件驱动的机制(想想PropertyChangeSupport和PropertyChangeListener的标准jdk库),这些都是缺less的。 我相信有一个扩展模块,并决定继续我自己的。 你可以谷歌它propertysupportsupport gwt和使用它或走我的方法。

我的方法涉及以MessageHandler和GWTEvent为中心的逻辑。 这些function分别与PropertyChangeListener和PropertyChangeEvent的function相同。 由于稍后解释的原因,我必须对其进行定制。 我的devise涉及MessageExchange,MessageSender和MessageListener。 交换机作为一个广播服务,将所有的事件发送给所有的听众。 每个发件人触发由Exchange监听的事件,并且交换器再次触发事件。 每个听众倾听交换,并根据事件决定自己(处理或不处理)。

不幸的是,GWT中的MessageHandler遇到了一个问题:“当一个事件正在被消耗,没有新的处理程序可以被挂钩”。 在GWT表格中给出的原因:持有处理程序的后备迭代器不能被另一个线程同时修改。 我不得不重写GWT类的自定义实现。 这是基本的想法。

我已经发布了代码,但是现在我正在去机场的途中,会尽快发布代码。

EDIT1:

还没有能够得到实际的代码,掌握了我正在为devise文档工作的一些功率点幻灯片,并创build了一个博客条目。

发布我的博客文章的链接: GXT-GWT App

EDIT2:

最后一些代码汤。 过帐1 过帐2 过帐3

请看: http : //www.gwtproject.org/javadoc/latest/com/google/gwt/event/shared/EventBus.html

(过时http://www.gwtproject.org/javadoc/latest/com/google/web/bindery/event/shared/EventBus.html

它应该与GWT运行良好,因为我现在自己尝试。