Hibernate, sub-classing and the visitor pattern

717 views Asked by At

I might be using the wrong word so when I say Business Object (BO) I mean a class with a reference to the class that's mapped to a database-table with Hibernate, as well as business logic.

The problem I'm facing is instantiating the right BO for subclasses without using reflection or instanceof.

For example imagine I've got a Pen-table with a single reference to an Animal-table which in turn have got two sub-tables Cat and Dog (all one-to-one references). The classes look somewhat like this:

public class Pen {
    private Animal a;
    // Getters and setters
}
public class Animal {
    // Getters and setters
}
public class Dog extends Animal {
    // Getters and setters
}
public class Cat extends Animal {
    // Getters and setters
}
public class PenBO {
    private Pen p;
    public AnimalBO getAnimalBO() { ... }
}
public interface Action {
    void visit(DogBO dbo);
    void visit(CatBO cbo);
}
public class Sound implements Action {
    void visit(DogBO dbo) { ... }
    void visit(CatBO cbo) { ... }
}
public interface AnimalBO {
    void accept(Sound s);
}
public class DogBO implements AnimalBO {
    Dog d;
    void accept(Sound s) {
        s.visit(this);
    }
}
public class CatBO implements AnimalBO {
    Cat c;
    void accept(Sound s) {
        s.visit(this);
    }
}

Then I just work with the BOs instantiating them like this inside the get-methods of the BOs:

Pen p = ... // Get or load from database 
PenBO pbo = new PenBO();
pbo.setPen(p);

Then I use the classes like this:

pbo.getAnimalBO().accept(new Sound());

It's the getAnimalBO-method that I'm working on. I want it to return the right instance of the BOs based on the Animal-instance of Pen.

I "could" use instanceof checking on the actual Animal from the current Pen but that's obviously not pretty. Another alternative I was thinking of was using reflection to get the class-name and add "BO" afterwards and get an instance of that, however it's also pretty ugly.

I've tried wrapping another visitor-pattern around getAnimalBO but it can't pick the right visit-method without casting and I don't want to add accept-methods to the non-BO classes.

If there's no clever way to get that method to work efficiently is something wrong at the core? I haven't really found any best practices for Hibernate. Some examples of Hibernate and the visitor pattern just add the accept-methods to the mapped classes which can't be good...

1

There are 1 answers

1
David M On BEST ANSWER

Your problem is actually simply an OO problem with inheritance, and transforming from one class hierarchy to a parallel one. You've already solved the database modelling of this and the Hibernate mapping, and there's nothing wrong with your visitor implementation obviously. It's just that you don't want to apply the visitor pattern to the mapped class hierarchy, and so you have created yourself a problem in transforming between two hierarchies.

As things stand, ome piece of your code is going to have to take responsibility for knowing that CatBO corresponds to Cat etc, and for knowing that if the Pen object's Animal property is actually a Cat, then the containing PenBO object's AnimalBO property will actually be a CatBO. Given that, it has to be able to determine the type of the Animal instance returned, so I can't see how you can avoid using one of either instanceof or reflection. What's your objection to instanceof? Reflection to get the class name and appending "BO" is very nasty and ugly, I agree, though it could obviously be made to work. But a single method whose responsibility is to take an Animal and return a suitable AnimalBO by using instanceof and selecting the right BO class isn't a bad thing in the circumstances IMHO.

The question I would ask is why you're mapping from one class hierarchy to another like this at all. You've said you don't want to add the accept methods to the mapped classes, and that it "can't be good", but you haven't justified why you feel this way. Is it any worse than the options you've presented yourself with already? By adding the visitor pattern to the mapped class hierarchy, you're not really embedding any business logic there, you're simply enabling the application of business logic across the hierarchy using a visitor pattern. Thus your business logic layer contains simple, generic visitor implementations, your mapped class or domain layer is POJOs plus the basic visitor implementation, and you do actually have the nice clean separation that I think you want.