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.)