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.)
2 comments:
Totally agree - puts the focus on the domain where it should be instead of the persistence engine.
That said, how do you move between implementations? Specifically, here is a common scenario:
1. User clicks a button
2. Handler looks at form fields and creates an instance of Foo
3. Handler passes aFoo to the persistence engine to store
after #3, it would be nice if the handler's ref to aFoo could be somehow magically switched to the instance of PersistentFoo without another step.
Good question. I see two approaches:
A. Magic. If Foo is an interface rather than a concrete class (but then FooData reappears as the form input version of Foo), then you can diddle. Ok, maybe this is deep magic.
B. Have step #3 return the new PersistedFoo and the handler needs to look like:
Foo data = getFooFrom(httpRequest);
Foo persisted = storeFooUsing(data);
Although what I'd actually do is:
Foo foo storeFooUsing(getFooFrom(httpRequest));
But I hate typing. :-)
Post a Comment