Java设计中if-else与instanceof的替代方案

5
我们的Java应用程序在后端使用Google Guava EventBus进行通信。其中一些事件使用Jersey的server-sent eventssupport发送到客户端,以启用通知。客户端只对某些类型的事件感兴趣,并且这些事件以JSON格式发送到客户端。
目前,我们正在使用if-elseinstanceof处理JSON体生成的巨大方法。 UIEvent仅用作过滤器的标记接口。
@Subscribe
public void handleEvent(final UIEvent event) {
  if (event instanceof A) {
    A a = (A) event;

  } else if (event instance B) {
    B b = (B) event;

  } ...
}

这段代码在系统添加越来越多的事件时变得混乱。经过一些研究,有一些替代方案但都不够好。
1)反射。
使用反射意味着我们可以使用声明性方式从事件对象中检索数据而不知道确切的类型。但使用反射不是类型安全的,在处理嵌套路径时可能会很混乱,例如 a.b.c
2)多态性
多态看起来是 instanceof 的一个很好的替代品,但在这种情况下真的有效吗?使用多态意味着向 UIEvent 接口添加像 toJSON 这样的方法。但这会逆转依赖流并将 UI 详细信息暴露给事件总线。
3)包装类
我还考虑使用事件包装类来封装单独的类中的 JSON 主体构建逻辑。然后在事件总线的 handleEvent 方法中,我可以获取事件对象的类型,并使用命名约定找到包装类,然后构造包装类实例,调用 toJson 方法以获取 JSON 主体。
public class AWrapper {
   public AWrapper(A a) {

   }

   public Object toJson() {

   }
}

这是我可以想到的最合理的方法。

需要建议和想法。


多态性是一个标准模式。然而,如果可能的话,你应该尝试测试事件中的字段并在其上进行切换。 - markspace
多态性也得到了我的支持。但是在接口中添加像 toJson 这样的方法会将你锁定在使用 json 上。另一个解决方案是添加一个返回通用数据属性集合(例如 Map<String, Object>)的方法,然后使用它来序列化为 json。 - Jorn Vernee
现代的库比如Jackson或者Gson可以轻松地将POJO转换成JSON,所以你不需要为此编写特殊的方法。为什么不创建一个自定义注解来区分内部事件和需要向客户端广播的事件呢?然后在handleEvent()中使用反射来检查注解是否存在。 - Mick Mnemonic
@markspace 我不想使用多态的原因是事件类在核心模块中,同时也被其他模块使用,但将其序列化为JSON的逻辑在API模块中。我不希望核心模块知道API模块的存在。 - Fu Cheng
@MickMnemonic 发送到客户端的JSON主体具有预定义的格式,因此简单的JSON序列化是不够的,需要进行数据转换。我以前没有考虑过自定义注释,看起来是区分不同事件的好选择。 - Fu Cheng
Eclipse经常使用一种模式,您可以借鉴其第三个选项:适配器,特别是相关的IAdaptableFactoryIAdapterManager类型。该模式可用于将事件类型适应为可序列化到所选格式的类型,而无需在事件类中添加其他职责。 - erickson
1个回答

1
我认为Google Guava EventBus的设计是为了避免你不得不使用许多if-else-if定义此类方法:

一些人提议为EventBus监听器创建通用的Handler接口。这会遇到Java中类型擦除的问题,更不用说可用性方面的问题了。

...

由于擦除,没有单个类可以使用不同的类型参数实现通用接口。这是从传统的Java事件中迈出的巨大一步,即使actionPerformed和keyPressed并不是非常有意义的名称,至少你可以实现两种方法!

通过创建自己的标记,你正在重新创建他们试图避免的问题。

对我来说,这就是Guava建议如何使用它的方式:

EventBus eventBus = new EventBus();
eventBus.register(new Object(){
    @Subscribe
    public void handleEvent(A a) {
        System.out.println("a");
    }
});

eventBus.register(new Object(){
    @Subscribe
    public void handleEvent(B b) {
        System.out.println("b");
    }
});

...

eventBus.post(new A());
eventBus.post(new B());

每个事件类型对应一个处理程序。显然,订阅者不需要像这个示例中的匿名类一样。

其他示例

http://tomaszdziurko.pl/2012/01/google-guava-eventbus-easy-elegant-publisher-subscriber-cases/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接