2

我会用一个例子来解释。我的 GWT 项目有一个公司模块,允许用户添加、编辑、删除、选择和列出公司。

其中,添加、编辑和删除操作将用户返回到 CompanyList 页面。因此,拥有三个不同的事件 -CompanyAddedEvent, CompanyUpdatedEvent and CompanyDeletedEvent以及它们各自的事件处理程序 - 对我来说似乎有点矫枉过正,因为它们的功能绝对没有区别。

让一个事件管理三个操作可以吗?我认为另一种选择是使用一些事件,例如CompanyListInvokedEvent. 但是,在我认为不合适的地方,事件实际上不是被调用的列表,而是被添加/更新/删除的公司。

如果它只是一个模块,我会通过三个单独的事件来完成任务。但其他 10 个这样的模块正面临这种困境。这意味着 10x3 = 30 个事件类以及它们各自的 30 个处理程序。这个数字足够大,我可以重新考虑。什么是一个好的解决方案?

更新 -

@ColinAlworth 的回答让我意识到我可以轻松地使用泛型而不是愚蠢的解决方案。以下代码表示一个事件 EntityUpdatedEvent,每当更新实体时都会引发该事件。

事件处理程序类 -

public class EntityUpdatedEvent<T> extends GwtEvent<EntityUpdatedEventHandler<T>>{

    private Type<EntityUpdatedEventHandler<T>> type;
    private final String statusMessage;

    public EntityUpdatedEvent(Type<EntityUpdatedEventHandler<T>> type, String statusMessage) {
        this.statusMessage = statusMessage;
        this.type = type;
    }

    public String getStatusMessage() {
        return this.statusMessage;
    }

    @Override
    public com.google.gwt.event.shared.GwtEvent.Type<EntityUpdatedEventHandler<T>> getAssociatedType() {
        return this.type;
    }

    @Override
    protected void dispatch(EntityUpdatedEventHandler<T> handler) {
        handler.onEventRaised(this);
    }
}

事件处理程序接口 -

public interface EntityUpdatedEventHandler<T> extends EventHandler {
    void onEventRaised(EntityUpdatedEvent<T> event);
}

将处理程序添加到事件总线 -

eventBus.addHandler(CompanyEventHandlerTypes.CompanyUpdated, new EntityUpdatedEventHandler<Company>() {

    @Override
    public void onEventRaised(EntityUpdatedEvent<Company> event) {
        History.newItem(CompanyToken.CompanyList.name());
        Presenter presenter = new CompanyListPresenter(serviceBundle, eventBus, new CompanyListView(), event.getStatusMessage());
        presenter.go(container);
    }
});

同样,我还有另外两个已添加和已删除的通用事件,从而消除了与事件相关的代码库中的全部冗余。

对此解决方案有什么建议吗?

PS >这个讨论提供了关于这个问题的更多见解。

4

1 回答 1

4

为了回答这个问题,让我首先提出另一种思考同一类问题的方式——我们将使用方法而不是事件。

在我的分层应用程序中,两个模块通过一个接口进行通信(请注意,这些方法都是void,所以它们很像事件 - 调用者不希望得到回复):

package com.acme.project;

public interface CompanyServiceInteface {
  public void addCompany(CompanyDto company) throws AcmeBusinessLogicException;

  public void updateCompany(CompanyDto company) throws AcmeBusinessLogicException;

  public void deleteCompany(CompanyDto company) throws AcmeBusinessLogicException;
}

这对我来说似乎有点矫枉过正——为什么不把这个 API 的大小减少到一种方法,并添加一个 enum 参数来简化它。这样,当我构建一个替代实现或需要在我的单元测试中模拟它时,我只有一个构建方法而不是三个。当我制作应用程序的其余部分时,这显然是多余的——为什么不只ObjectServiceInterface.modify(Object someDto, OperationEnum invocation);为所有 10 个模块工作呢?


一个答案是,您可能想要彻底修改其中一个的实现,而不是其他的 - 现在您已将其简化为仅一种方法,所有这些都属于该 switch 案例。另一个是,一旦以这种方式简化,往往会进一步简化——也许将其合并createupdate一种方法。完成此操作后,所有调用点都必须确保履行该方法合同的所有可能细节,而不仅仅是一个特定的细节。

如果这些事件的接收者很简单并且仍然如此,那么可能没有充分的理由不只拥有一个对于所有可能的用例来说显然足够通用的 ModelModifiedEvent - 也许只是包装 ID 以请求所有客户端模块刷新它们该对象的视图。如果将来出现只有一种事件很重要的用例,那么现在必须更改该事件,所有导致该事件被创建的站点也必须更改,以便它们正确填充这个新字段。

Java 商店通常不使用 Java,因为它是最漂亮的语言,或者因为它是最容易编写或找到开发人员的语言,而是因为它相对容易维护和重构。在设计 API 时,重要的是要考虑未来的需求,还要考虑修改当前 API 需要什么——你的 IDE 几乎肯定有一个快捷键来查找特定方法或构造函数的所有调用,允许你轻松找到所有使用它的地方并更新它们。因此,请考虑您期望的其他用例,以及更新代码库的其余部分的难易程度。

最后,不要忘记泛型——对于我上面的例子,我可能会做一个 DtoServiceInterface 来简化事情,所以我只用三个方法声明一个接口,并实现它并根据需要引用它。以同样的方式,您可以制作一组三种GwtEvent类型(带有*Handler接口,也可能Has*Handlers有),但保持它们对所有可能的类型都是通用的。在这里考虑com.google.gwt.event.logical.shared.SelectionEvent<T>一个示例-在您的情况下,您可能希望将模型对象类型设置为参数,以便处理程序可以检查他们正在处理的事件类型(请记住,泛型在 Java 中被删除),或者从一个EventBus为每个模型类型。

于 2013-03-23T18:32:39.193 回答