Friday, October 01, 2004

Upside-down inheritance

Here is a classic, persisted object in Java:

public class Foo extends Persisted {
    // fields

    // constructors

    // getters, setters

Pretty dull. What's wrong with that?

Now here's a typical query-by-example (QBE) method in some finder class (assuming something suitable like Hibernate or iBatis, and a supporting framework):

public Foo findFooByExample(Foo foo) {
    return (Foo) findByExample(FOO_TABLE, foo);

And here's a typical data transfer object (DTO) for passing around the found Foo to some other layer of the program:

public class FooData {
    // same fields as Foo

    // same constructors as Foo

    // same getters, setters as Foo

And then there's methods which take a Foo and need testing:

public void doBar(Foo foo) {
    // Does Bar look at anything in Persisted, or just Foo?
    // The test code needs to mock the persisted methods, if so.  Rats.

Oh, wait. Hrm. Lots of code duplication, lots of overhead to make changes, extra things to test. This is not looking so good. Why is that?

The inheritance is upside-down!

Once you disabuse yourself of the preconception that domain/database objects (DO) extend a persistence base class, the solution is trivial:

public class Foo {
    // fields

    // constructors

    // getters, setters

public class FooPersisted extends Foo implements Persisted {
    // fields, getters, setters for persistence

Now the QBE example uses Foo for input, and FooPersisted for output; FooData goes away completely; and the doBar method explictly requires either a Foo or a FooPersisted, making it clear if it does or does not fiddle with persistence.

And a bonus: it is almost always less work to implement the two or three fields and getter/setters which persistence uses rather than the larger number of fields which the DO uses. And as they are the same few fields everywhere, you can automate the process using code generation or annotations.

(Aside: Or course, it would be even easier still if Java only supported mixins. Then you just defined FooPersisted as:

public class FooPersisted extends Foo, Persisted {
    // empty -- no further code needed

No new keywords; no confusion—super always refers to the first class mentioned in the extends list.

But that is a different post.)

Post a Comment