Dirty Deeds, Done Dirt Cheap (with Reflection and Bytecode Generation)
Have been doing some marathon hacking on mixins this weekend. My girlfriend is sort of annoyed with me, and I am hungry. But, an initial implementation of a derned cool idea is done. I am not bothering to make it available as a better implementation is half done, but my focus finally wore off (I identified with the Focused a la Vernor Vinge this weekend).
It is pretty cool, but not super straightforward to explain. It works like any other mixin library where you can specify an interface and an implementation and mix combos together to get one object back with delegation out to the various mixed components. It goes a step beyond though.
Back in October, Rickard posted an blog entry with an interesting idea on mixins which I just re-read earlier this week. The idea I took away (related to this) was that a mixin could declare interfaces it required, but didn't implement. The implementations of the other methods would be provided at runtime and the whole object would be satisfied by the eventual mixed object.
So, what I did was set it up so that invocations against the unimplemented interfaces in the implemented methods get delegated back out to the top level mixed instance so that the calls could be in fact executed against the implementations of those interfaces. This turned out to be trickier to do than I would have liked (without resorting to implementing the library in AspectJ, which actually would have been acceptable as it wouldn't require the client to use it, but I digress) mostly because I am a bear of little brain and keeping track of complex things makes me hurt.
The initial implementation (ie, the one that works against all my initial test cases, which I finished sometime this morning) was built up from the interceptor mixin tool which handles everything except these dirty deeds. As such it is very interface -> implementation oriented. This falls apart for the more sophisticated requirements (which I discovered after I got the intial tests passing, eventually) as it requires that an implementation actually implement everything in one of its interfaces if it wants to recieve calls for that interface. This works for most cases, but is not the real goal.
The real goal is to be able to create, say, a mixin based Collection
where individual mixins implement parts of the Collection
interface. You probably wouldn't use this to build collections, but that is what I am using for my unit tests (and a couple extra interfaces for testing inheritance of interfaces). This is trickier than I like without resorting to full aspect implementations as I really want wormholes, and the workaround is just ugly (because I require that it also work with the more convential interface -> impl type mixin additions). I am getting very familiar with CGLIB while doing this, anyway =)
The one that will be released is partially implemented, but as I said, brain stopped functioning and body made me aware that it needs nourishment. I love it when I get this focused on a problem, but I hate it too as I am unable to focus on anything else at the same time.
I also half wish Rickard would open source that library he is working on as i think it does what I want, from reading his blog. On the other hand, it is really fun to write this stuff and I probably wouldn't if it were already done. OTOH, he mentioned XML configuration, so maybe I would just re-implement it.