带有 instanceof 的 if-else 的 Java 设计替代方案
Posted
技术标签:
【中文标题】带有 instanceof 的 if-else 的 Java 设计替代方案【英文标题】:Java design alternatives to if-else with instanceof 【发布时间】:2017-01-19 08:24:58 【问题描述】:我们的 Java 应用程序在后端使用 Google Guava EventBus 进行通信。其中一些事件使用 Jersey 的 server-sent events support 发送到客户端以启用通知。客户端只对某些类型的事件感兴趣,这些事件以 JSON 格式发送到客户端。
目前我们正在使用if-else
和instanceof
以一种巨大的方法处理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
的一个很好的替代方案,但在这种情况下确实有效。使用多态意味着将toJSON
之类的方法添加到UIEvent
接口。但这会恢复依赖流并将 UI 细节暴露给事件总线。
3) 包装类
我还在考虑使用事件包装类将 JSON 主体构建逻辑封装在单独的类中。然后在事件总线的handleEvent
方法中,我可以得到事件对象的类型并使用命名约定找到封装类,然后构造封装类实例,调用toJson
方法得到JSON体。
public class AWrapper
public AWrapper(A a)
public Object toJson()
这是迄今为止我能想到的最合理的方法。
需要建议和想法。
【问题讨论】:
Polymorphism 是这里的标准模式。但是,如果可能的话,您应该尝试在事件中测试一个字段并打开它。 多态性也有我的投票权。但是在接口上使用toJson
之类的方法会锁定您使用 json。您可以考虑的另一个解决方案是添加一个返回一组通用数据属性(例如Map<String, Object>
)的方法,然后您可以使用该方法将其序列化为 json。
像 Jackson 或 Gson 这样的现代库可以毫不费力地将 POJO 序列化为 JSON,因此您不需要特殊的方法。为什么不创建一个自定义注释来区分内部事件和需要广播给客户端的事件?然后,您将在 handleEvent()
中使用反射来检查注释是否存在。
@markspace 我不想使用多态的原因是因为事件类在核心模块中,也被其他模块使用,但是对 JSON 逻辑的序列化在 API 模块中。我不想让核心模块知道 API 模块。
@MickMnemonic 发送到客户端的 JSON 正文有预定义的格式,所以简单的 JSON 序列化是不够的,需要进行数据转换。之前没考虑过自定义注解,看起来是区分不同事件的好选择。
【参考方案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/
【讨论】:
以上是关于带有 instanceof 的 if-else 的 Java 设计替代方案的主要内容,如果未能解决你的问题,请参考以下文章