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:
Which i use like this:
(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:
(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) :
For managing these lifecycles I use the DDP with four back-end proxies (one per state).
This is some actual muscle flexing!
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 -> ...
For managing these lifecycles I use the DDP with four back-end proxies (one per state).
This is some actual muscle flexing!