是否有推荐的方法在使用 GWT 的 MVP 中使用观察者模式?

2023-12-30

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

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

  • 演示者对 UI 技术一无所知(即没有使用 com.google.* 中的任何内容)
  • 该视图对演示者一无所知(还不确定我是否希望它与模型无关)
  • 模型对视图或演示者一无所知(......显然)

我会在视图和演示者之间放置一个接口,并使用观察者模式来解耦两者:视图生成事件,演示者收到通知。

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


程序结构

我就是这样做的。这Eventbus让演示者(扩展抽象类Subscriber) 订阅属于不同的事件modules在我的应用程序中。每个module对应于我系统中的一个组件,每个模块都有一个事件类型、一个呈现器、一个处理程序、一个视图和一个模型。

订阅该类型所有事件的演示者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按钮应该链接到与操作相对应的某些行为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*/ }

Example

考虑一个简化的 Eclipse,其中左侧有一个类层次结构,右侧有一个代码文本区域,顶部有一个菜单栏。这三个将是由三个不同的演示者提供的三个不同的视图,因此它们将组成三个不同的模块。现在,文本区域完全有可能需要根据类层次结构中的更改进行更改,因此文本区域呈现器不仅订阅从文本区域内触发的事件,还订阅事件是有意义的被从类层次结构中解雇。我可以想象这样的事情(对于每个模块,都会有一组类 - 一个处理程序、一个事件类型、一个演示者、一个模型和一个视图):

public enum Module 
{
   MENU,
   TEXT_AREA,
   CLASS_HIERARCHY
}

现在考虑一下,我们希望在从层次结构视图中删除类文件时正确更新视图。这应该会导致 GUI 发生以下变化:

  1. 应该从类层次结构中删除该类文件
  2. 如果类文件已打开,因此在文本区域中可见,则应将其关闭。

两个演示者(一个控制树视图,一个控制文本视图)都会订阅从CLASS_HIERARCHY模块。如果事件的动作是REMOVE,两位演示者都可以采取适当的行动,如上所述。控制层次结构的演示者可能还会向服务器发送一条消息,以确保已删除的文件确实被删除。这种设置允许模块通过侦听从事件总线激发的事件来对其他模块中的事件做出反应。几乎没有发生耦合,并且交换视图、演示者或处理程序是完全无痛的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否有推荐的方法在使用 GWT 的 MVP 中使用观察者模式? 的相关文章

随机推荐

  • PHP 无法通过 COM 打开 Excel 工作簿

    该代码工作正常 直到到达打开的工作簿行 然后失败
  • 如何创建扩展方法 (F#)? [复制]

    这个问题在这里已经有答案了 如何在 F 中创建扩展方法 例如 如下 C 扩展 public static string Right this string host int index return host Substring host
  • 将 HTML 分成页面,分割长段落

    好吧 我什至不确定这是否可以用我当前的方法来完成 我正在尝试将 HTML 文档的内容放入当前视口大小的页面中 我目前正在通过迭代文档的所有元素并检查它们的顶部偏移量是否在当前页面的边界内来执行此操作 只要不在当前页面的边界内 此偏移量就会成
  • Typescript:是否有一种简单的方法将一种类型的对象数组转换为另一种类型

    所以 我有两节课 Item name string desc string meta string ViewItem name string desc string hidden boolean 我有一个 Item 数组需要转换为 View
  • 实体方面(春季)

    我在定义我的方面时遇到了一些问题 我有一堆实体 我想在其中分析 get methods 所以我编写了以下切入点和方法 Pointcut execution tld myproject data entities get public voi
  • 在运行时创建/修改枚举

    我正在创建一个程序 用户可以选择创建自己的自定义属性 这些属性最终将显示在PropertyGrid 现在我不想搞乱自定义编辑器 所以我只允许原始类型属性 string int double DateTime bool等 PropertyGr
  • 无闪烁文本框

    我的表单上有一个简单的 Winforms 多行文本框 每当我调整文本框的大小或移动文本框时 它的内容就会开始疯狂地闪烁 这看起来非常恶心 甚至可能导致某些用户癫痫发作 有没有办法操纵文本框的重绘过程来消除闪烁 我找到了一个工作解决方案 ht
  • 连接到 SQL Server 2008 的最大并发用户数

    连接到 SQL Server 2008 的最大并发用户数是否有限制 我猜测计算机资源决定了该限制 如果存在 但我也想知道是否存在理论上的限制 我尝试用谷歌搜索答案 但我找到的所有信息都是基于 SQL Server 的早期版本 如果有限制 它
  • 如何在 ASP.NET Core 中重写 HandleUnauthorizedRequest

    我正在将项目迁移到 asp net core 但我一直在迁移控制器的自定义授权属性 这是我的代码 public class CustomAuthorization AuthorizeAttribute public string Url g
  • 采用 {ID}-{Slug} 格式的 ASP.NET MVC 路由约束

    我有一条如下所示的路线 理想情况下我希望它匹配 domain com layout 1 slug is the name of the page routes MapRoute Layout Route name layout id slu
  • C# 7.0 中的泛型函数和 ref 返回

    是否可以使用 C 7 0 中的 ref returns 功能定义一个通用函数 该函数可以对对象的两个实例中的字段进行比较和更新 我正在想象这样的事情 void UpdateIfChanged
  • avconv:根据图像的子集制作视频

    我正在尝试使用 avconv 将一堆 png 制作成视频 png 的编号如下filename
  • 如何检测难以捉摸的 64 位可移植性问题?

    我在我为 64 位端口准备的一些 C 代码中发现了与此类似的片段 int n size t pos npos initialization while pos find ch start npos advance start positio
  • KineticJS:右键单击触发单击

    我正在使用 Kineticjs 并且定义了一个像这样的矩形事件 this rect on click tap function foo 当我左键单击时会触发该事件 右键单击时也会触发该事件 如何避免右键单击触发此事件 我无法禁用页面中的右键
  • 如何获取本机 Oracle 函数(例如 NVL、ABS 等)的列表

    我尝试使用下面的命令 但没有按我的预期工作 select OBJECT NAME OWNER from SYS ALL OBJECTS where upper OBJECT TYPE upper FUNCTION order by OWNE
  • CSS 过滤器背后的数学原理是什么? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 假设这些是我应用于图像的滤镜 我想知道这些过滤器背后的数学原理 filter contrast 1 3 brightness 0 8
  • 使用 Jsonata 匹配名称包含空格或特殊字符的 json 字段

    假设我有一些 JSON 例如 normal 1 has some spaces 2 3 我可以获得的值normal带有简单查询的字段 normal 但是我无法获取其他两个字段 我努力了 has some spaces has some sp
  • 对具有相同第一列值的 2D numpy 数组元素进行分组

    我有一个像这样的 2D numpy 数组 569 897 570 898 570 900 571 901 571 905 572 906 我想要要分组的第一列中具有相等值的元素按照以下方式一起 569 897 570 898 570 900
  • 将查询结果追加到表中

    当我尝试通过 API 插入查询作业 将查询结果附加到另一个表 时 我现在遇到了一些问题 查看文档 这似乎可以通过插入查询作业 设置目标表并将 writeDisposition 设置为 WRITE APPEND 来实现 如下所示 config
  • 是否有推荐的方法在使用 GWT 的 MVP 中使用观察者模式?

    我正在考虑使用 GWT 根据 MVP 模式实现用户界面 但对如何进行有疑问 这些是我的 一些 目标 演示者对 UI 技术一无所知 即没有使用 com google 中的任何内容 该视图对演示者一无所知 还不确定我是否希望它与模型无关 模型对