JPA, inheritance and instanceof



我使用JPA继承和JOIN策略(JPA2/HHibernate)。我有一个抽象的通用Event实体,它有共享的字段(日期、时间、地点等)及其子项,比如OutdoorEvent、ClassicalMusicEvent等,每种类型都有特定的字段。我在所有事件上进行搜索,得到一个我显示的List<Event>。不过,每个事件类型的处理是不同的,所以我需要弄清楚每个event对象的事件类型。现在问题来了。我想出了两个解决方案。首先,instanceof关键字:

if (event instanceof OutdoorEvent) {
   ...
} else if (event instanceof OtherKindOfEvent) {
   ...
} etc.

其次,我向Event实体添加一个瞬态枚举字段,并在每个子类型的构造函数中设置该字段。然后我可以做:

if (event.getType() == EventType.OutdoorEvent) {
   ...
} else if (event.getType() == EventType.OtherKindOfEvent) {
   ...
} etc.

哪个解决方案更好,或者更OOP?对此还有其他解决方案吗?

这是一个很好的问题,因为最佳OOP解决方案是使用多态性。在抽象Event类上添加一个抽象的"process"方法,然后在子类中实现所需的处理。然后你可以直接调用process(),而不在乎它是什么类型的子类

然而,您可能希望将Event(即数据)类与逻辑解耦,这样您可能会在某个地方执行instanceof或类似于枚举的操作。

我没有偏好,有可能一个比另一个更快,我可能会选择enum而不是instanceof来获得速度(很想听听是否有人对此有见解)。

如果你确实使用了enum。在你的事件示例中,你应该使用一个开关,而不是If.else.

最OOP的方法是使用访问者模式。

将显示处理逻辑放入访问者:

public VisitorImpl implements Visitor {
    public void handleOutdoorEvent(OutdoorEvent event) { .. }
    public void handleOtherKindOfEvent(OtherKindOfEvent event) { .. }
}

然后在每个子类上都有:

public void handleDisplay(Visitor visitor) {
     visitor.visit(this);
}

然后,你只需要做多次if检查:

Visitor visitor = new VisitorImpl(..);
for (..) {
   entity.visit(visitor);
}

除此之外,instanceof看起来更干净。

如果您处理Hibernate,那么您应该为使用Hibernate代理的情况做好准备(例如,请参见将Hibernate代理转换为子类):在您的示例中,变量event可能是动态创建的Hibernate代理实例,在这种情况下event instanceof OtherKindOfEvent将不起作用。

所以,您要么需要去代理(使用Hibernate实用程序并放松JPA抽象),要么使用前面提到的方法。所以我个人的偏好是第二种选择(或者类似的选择)。

简单的策略模式:

Map<Class<?>,Handler> handlers = new ArrayList();
handlers.add(SomeClass1.class, new Handler1());
handlers.add(SomeClass2.class, new Handler2());
handlers.add(SomeClass3.class, new Handler3());
....

for(Entity e : entities){
   handlers.get(e.getClass()).execute(e);
}

最新更新