JPA, inheritance and instanceof

2019-05-06 03:58发布

问题:

I use JPA inheritance with JOIN strategy (JPA2/Hibernate). I have an abstract general Event entity with shared fields (date, time, place etc), and its children, let's say OutdoorEvent, ClassicalMusicEvent etc. with specific fields for each type. I make a search over all Event's, getting a List<Event> which I display. The processing for each event type is different though, so I need to figure out the type of event for each Event object. Now here's the problem. I came up with two solutions. First, the instanceof keyword:

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

Second, I add a transient enum field to the Event entity and set this field in every children type's constructor. Then I could do:

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

Which solution is better, or more OOP? Is there any other solution to this?

回答1:

This is a good question because the optimal OOP solution would be to use polymorphism. On your abstract Event class add an abstract 'process' method and then in your subclasses implement the required processing. Then you can just call process() and don't care what the type of subclass it is.

However, you probably want to keep your Event (i.e. data) classes decoupled from the logic so somewhere you're probably going to end up doing instanceof or something like your enum.

I have no preference which, it is possible that one is faster than the other and I would probably go with the enum rather than instanceof for speed (keen to hear if anyone has insight on that).

If you do go with the enum In your example with the events, you should use a switch instead of if..else.



回答2:

If you deal with Hibernate then you should prepare yourself for cases with Hibernate Proxies (see e.g. Casting Hibernate Proxies to Subclasses): in your example variable event might be a dynamically created instance of HibernateProxy, and event instanceof OtherKindOfEvent will not work in this case.

So you will either need to de-proxy (using Hibernate utilities and loosing JPA abstraction), or use approach like you've mentioned. So my personal preference is the second option (or anything similar to it).



回答3:

The most OOP approach would be to use the visitor pattern.

Put the display handling logic in a Visitor:

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

And then on each subclass have:

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

Then instead of the multiple if-checks you just do:

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

Apart from that, instanceof looks cleaner.



回答4:

Simple strategy pattern:

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);
}