程序结构
这就是我做的。该
Eventbus让主持人(扩展抽象类
Subscriber)订阅属于不同的事件 模块 在我的应用程序。每个 模块
对应于我系统中的一个组件,并且每个模块都有一个事件类型,一个演示者,一个处理程序,一个视图和一个模型。
订阅所有类型的事件的演示者
CONSOLE将接收从该模块触发的所有事件。对于更细粒度的方法,您总是可以让演示者订阅特定的事件,例如
newlineAddedEvent诸如此类,但是对于我来说,我发现在模块级别进行处理就足够了。
如果愿意,可以异步调用演示者的紧急救援方法,但是到目前为止,我发现自己几乎不需要这样做。我想这取决于您的确切需求。这是我的
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事件转换为专门用于我的系统的事件。下面的处理程序
ClickEvents只需将它们包装在自定义事件中,然后将其触发以
EventBus供订阅者处理即可。在某些情况下,对于处理程序来说,在触发事件之前甚至有时在确定天气或不发送事件之前执行额外的检查是有意义的。当将处理程序添加到图形组件时,将给出处理程序中的操作。
public class AppHandler extends ScHandler{ public AppHandler(Action action) { super(action); } @Override public void onClick(ClickEvent event) { EventBus.get().fire(new AppEvent(action)); }Action是一个枚举,表示系统中数据操作的可能方式。每个事件都以初始化
Action。演示者使用该操作来确定如何更新其视图。具有操作的事件
ADD可能会使演示者向菜单添加新按钮,或向网格添加新行。
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; }演示者订阅了属于不同模块的事件,然后在它们被激发时对其进行抢救。我还让每个演示者为其视图定义一个接口,这意味着演示者将永远不必了解实际的图形元素。
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负责为每个视图创建正确类型的处理程序。每个工厂都以实例化,
Module用于创建正确类型的处理程序。
public ScHandler create(Action action){ switch (module) { case ConSOLE : return new ConsoleHandler(action);该视图现在可以自由地向其组件添加不同类型的处理程序,而无需了解确切的实现细节。在此示例中,所有视图需要知道的是
addButton按钮应该链接到与action对应的某些行为
ADD。这种行为的方式将由捕获事件的演示者决定。
public class AppView implements Display public AppView(HandlerFactory factory) { ToolStripButton addButton = new ToolStripButton(); addButton.addClickHandler(factory.create(Action.ADD)); } public void openDrawer(String text) { } public void closeDrawer() { }例
考虑一个简化的Eclipse,其中您有一个类层次结构在左侧,一个代码文本区域在右侧,而菜单栏位于顶部。这三个是由三个不同的演示者组成的三个不同的视图,因此它们将构成三个不同的模块。现在,完全有可能需要根据类层次结构中的更改来更改文本区域,因此,对于文本区域演示者来说,不仅要订阅从文本区域内触发的事件,而且还要订阅事件,这是有意义的从类层次结构中被解雇。我可以想象这样的事情(每个模块会有一组类-
一个处理程序,一个事件类型,一个演示者,一个模型和一个视图):
public enum Module { MENU, TEXT_AREA, CLASS_HIERARCHY}现在考虑我们希望我们的视图在从层次结构视图中删除类文件时正确更新。这将导致对gui进行以下更改:
- 该类文件应从类层次结构中删除
- 如果类文件已打开,因此在文本区域中可见,则应将其关闭。
两名演示者,一个控制树视图,一个控制文本视图,都将订阅从
CLASS_HIERARCHY模块触发的事件。如果事件的动作是
REMOVE,则两个主持人都可以采取适当的动作,如上所述。控制层次结构的演示者可能还会向服务器发送一条消息,以确保已删除的文件实际上已被删除。此设置允许模块仅通过侦听从事件总线触发的事件来对其他模块中的事件做出反应。进行的耦合很少,交换视图,演示者或处理者是完全轻松的。



