Windsor, Decorators and Chains of Responsabilityes, Oh YEAH!
A few days ago I made a small change to Windsor, the change was basically stopping Windsor from trying to resolve a dependency using a dependency that it is already resolving. Now, this is prettry hard to explain without a good example, so consider this one:
Let us say that we have the standard Chain Of Responsability here, each finder will check its store for an item that matches the specification, and would call the next one in the chain if it can't. Now, previously you had to instruct Windsor explicitly about who was the next in the chain, something like this:
<components> <component id="cache_finder" service="IResultFinder`1" type="CacheResultFinder`1"> <parameters> <finder>${db_finder}</finder> </parameters> </component> <component id="db_finder" service="IResultFinder`1" type="DatabaseResultFinder`1"> <parameters> <finder>${ws_finder}</finder> </parameters> </component> <component id="ws_finder" service="IResultFinder`1" type="WebServiceResultFinder`1"> <parameters> <finder>${failed_finder}</finder> </parameters> </component> <component id="failed_finder" service="IResultFinder`1" type="FailedResultFinder`1"/> </components>
That is quite a bit of XML, and while it works, it make it awkward to understand what is going on in complex scenarios. The main issue here was that Windsor looked at CacheResultFinder<T>, saw that it accepted IResultFinder<T> and that it had a valid component that had this service (CacheResultFinder<T>) and promptly threw a dependency cycle exception. As a direct result of that, we had to manually override Windsor's selection, and also had to have a FailedResultFinder<T>, which must go on the end of the chain, again, because otherwise Windsor would try to resolve the ctor(IResultFinder<T> finder) constructor.
This is a workable solution, but I feel that this is telling the container way too much, why isn't it smart enough to figure it out? The problem with saying it about OSS software is that I can't really rant about it, since the answer is usually "we accept patches", bummer. So, I went ahead and implement this feature in Windsor, where it will not try to resolve what is currently being resolve. The end result is that for the scenario above we can give up on FailedResultFinder<T> and write just this:
<components> <component id="cache_finder" service="IResultFinder`1" type="CacheResultFinder`1"/> <component id="db_finder" service="IResultFinder`1" type="DatabaseResultFinder`1"/> <component id="ws_finder" service="IResultFinder`1" type="WebServiceResultFinder`1"/> </components>
We put the responsability for putting the chain of responsability together in the hand of the container, and it will build the system using simple first come fist on the chain approach, until it runs out of components that can satisfy the requirement, in which case it will use the default constructor.
Now this is much better, no?
Comments
Love it, that's so much easier to work with then the previous approach :) is it in the trunk already?
Hmm, sounds nice. But at the end, normally there is no ordering in XML-Elements is there? So the ordering is implicit. I think this will work, but maybe these chains should be grouped in a way that the chains can be found easyly.
Alex,
of course :-D
@Mark,
XML has strict ordering.
I don't follow the rest.
Hmm just to add. I like to document my solution. If the code/xml doesn't speak for itself I tend to add comments. What I do in the case of a responsability chain is.
<!-- Begin ABC chain --><component.../>
<!-- End ABC chain -->I'm not sure if there can be found a better way to document the chain with name etc... I was thinking about something like this...
<componentChain>
<component.../>
</componentChain>
But now mentioning this, I don't think this solution is desirable.
That's interesting, Ayende. Could you explain a little better what happened with the former approach? You say that a dependency cycle exception was thrown, was it handled by Windsor or what?
http://www.answers.com/responsibility&r=67
Simone,
It would throw, unless you would instruct it to seek a specific one, that is why you have all the <finder>${db_finder}</finder> elements.
I would agree that while this is a pretty clean answer, it seems like there should be more explicit indication that it's a chain -- either:
<chain>
<component .../>
<component .../>
</chain>
Or:
<component chain="A" .../>
<component chain="A" .../>
Otherwise, if your schema gets very complex, it might be difficult to discern which components are being resolved. For example, if you have:
<component service="IService" type="ServiceImpl1"/>
<component service="IAnotherService" type="FooImpl"/>
// ...another 50 component definitions...
<component service="IService" type="ServiceImpl2"/>
Wouldn't the second "chained" request for IService result in SerivceImpl2? This seems like it could be counter-intuitive...
@Nate,
If you got to this point, you really should split the configuration to separate files, and you can always override the default behavior
Great work! This is exactly what I was looking for!
I've actually run into this exact problem in the past. The work around I used was to make the constructor for each of the components in the chain take a simple Object, rather than the Interface type, and then do a cast.
This solution looks great! I would prefer something a little more explicit though in case you needed to select a specific service implementation later. Maybe the <chain> syntax mentioned earlier. Or even, just use the first XML. In my experience Windsor would through a cycle dependency exception in that situation as well.
Great stuff!
As other observed I think that transparently providing this behavior might be misleading. I'd opt for a more explicit syntax like the one suggested by Nate.
BTW, do you know how other IoC containers deal with this?
@Simone,
This is optional, but I like the syntax. Nate's second suggestion is possible today.
About other IoC, no idea, frankly.
Comment preview