Monday, February 06, 2006

Double Proxy Pattern

Here, I document a behavioral pattern which I often (not very often but quite often) use and which doesn't seem to be much used except by me. Maybe it's an anti-pattern.
Here is a classical implementation of the lazy-loading proxy:
interface Banana {
boolean isRipe();
}
interface BananaLoader {
Banana loadBanana();
}
class LazyBanana implements Banana {
private Banana banana;
private BananaLoader bananaLoader;

LazyBanana(BananaLoader bananaLoader) {
this.bananaLoader = bananaLoader;
}

boolean isRipe() {
if (banana == null) {
banana = loader.loadBanana();
}
return banana.isRipe();
}
}

Which i use like this:
Banana bananaNumberOne = new LazyBanana(new BananaLoader() {
Banana loadBanana() {
return myBananaStore.getById(1);
}
});
print(bananaNumberOne.isRipe()); // loading occurs now
print(bananaNumberOne.isRipe()); // no more loading

(The use of a closure-style anonymous class instance is, maybe, not so classical.)
The if bugs me (it feels awkward and out of place) so I found a way to code around it:
interface BananaReference {
Banana getBanana();
void setBanana(Banana banana);
}
class BananaLazySwapper implements Banana {
private BananaLoader bananaLoader;
private BananaReference bananaReference;

BananaLoader(BananaLoader bananaLoader, BananaReference bananaReference) {
this.bananaLoader = bananaLoader;
this.bananaReference = bananaReference;
}

boolean isRipe() {
Banana banana = bananaLoader.loadBanana();
bananaReference.set(banana);
return banana.isRipe();
}
}

class LazyBanana2 implements Banana, BananaReference {
private Banana banana;

LazyBanana2(BananaLoader bananaLoader) {
this.banana = new BananaLazySwapper(bananaLoader, this)
}

boolean isRipe() {
return banana.isRipe();
}

Banana getBanana() {
return banana;
}
void setBanana(Banana banana) {
this.banana = banana;
}
}

(In such a case (lazy loading), BananaReference can be avoided using anonymous inner class)

So I've got rid of the if which bugged me. I've traded it for a method lookup. I prefer that code smell.
Basically, the front-end proxy is a brain-dead proxy whose the proxied object can be replaced at runtime. The back-end proxy performs the actual loading and replace itself (in the first proxy) by the loaded value.
Over-architecturing?
The Double Proxy Pattern goes far beyond lazy objects. I've first used it while writting a recursive descent parser with memoization (à la Packrat plus fixed point detection).
Now, I'm using it to manage the lifecycle of persistent objects. A persistent object can have several states: transient, untouched, dirty, stub. There's two distinct lifecycles (depending on whether the object was already persistent) :
  • transient -> untouched -> dirty -> untouched -> dirty -> ...
  • stub -> untouched -> dirty -> stub -> untouched -> dirty -> ...
(In stub state the persistent object can not be in memory yet (lazy loading) or anymore (reclaimed by the garbage collector).)
For managing these lifecycles I use the DDP with four back-end proxies (one per state).

This is some actual muscle flexing!