In my previous post, I outlined an issue that I had with Castle Windsor for configuring multiple chains of responsibility. I want to have different ProcessConsumer classes that require a slightly different chain of processors:
ProcessConsumer1 -> Processor1 -> Processor2
ProcessConsumer2 -> Processor3 ->? Processor1 -> Processor2
Solution with Castle Windsor
For achieving this scenario using Castle Windsor, without it throwing a CircularDependencyException at me, I needed to split up the IProcessor interface and the BaseProcessor class like so:
public interface IProcessor { void Process(Request request); } public interface IChainableProcessor : IProcessor { IProcessor Successor { get; set; } } public abstract class BaseProcessor : IProcessor { public virtual void Process(Request request) { // Some base class behaviour FurtherProcess(request); } protected abstract void FurtherProcess(Request request); } public abstract class BaseChainableProcessor : BaseProcessor, IChainableProcessor { public IProcessor Successor { get; set; } public override void Process(Request request) { base.Process(request); if(null != Successor) { Successor.Process(request); } } }
Chainable processors can now derive from the BaseChainableProcessor class and those processors that should come last in the chain can continue to derive from the BaseProcessor class.
This approach no longer results in circular dependencies. Although it works, I don’t like this solution that much because now I can no longer change the order of responsibilities by simply altering the IoC container configuration. If I want to achieve that, I possibly have to change some code as well. This also means that some processors must always come last in the chain, which is not always feasible in complex scenarios.
Solution using StructureMap
One of the things I want to explore in the next couple of months is StructureMap. I was wondering whether this particular IoC container can solve this issue without having to split up the IProcessor interface and BaseProcessor class. It appears that StructureMap can indeed handle this scenario in a graceful way. I just needed to rip out Castle Windsor and replace it with the StructureMap goo. Below you can see the DependencyContainer class that I’ve been using for this example:
public class DependencyContainer { public void Configure() { ObjectFactory.Initialize(x => { x.UseDefaultStructureMapConfigFile = false; x.AddRegistry(new CORRegistry()); }); } public T Resolve<T>() { return ObjectFactory.GetInstance<T>(); } }
Really straightforward stuff. I derived a class from StructureMap’s Registry class and added it to the ObjectFactory. Here is the code for my derived registry class:
public class CORRegistry : Registry { protected override void configure() { ForRequestedType<IProcessor>() .TheDefault.Is.OfConcreteType<Processor1>() .WithName("Processor1") .SetterDependency<IProcessor>() .Is(y => y.OfConcreteType<Processor2>()); ForConcreteType<ProcessorConsumer1>() .Configure .CtorDependency<IProcessor>() .Is(x => x.TheInstanceNamed("Processor1")); ForConcreteType<ProcessorConsumer2>() .Configure .CtorDependency<IProcessor>() .Is(x => x.OfConcreteType<Processor3>() .SetterDependency<IProcessor>() .Is(y => y.TheInstanceNamed("Processor1"))); } }
That’s all I had to do. Everything just works now. I really like the way StructureMap handles things (registry classes) and its configuration is quite easy. This code could probably be improved as these are my first baby steps as a noob. This first pleasant experience certainly motivates me to do some more goofing around with StructureMap.
As always, I’m open for suggestions.
Till next time,
Jan, the injected
Update 17/11/2008: Some modifications after input from Jeremy. Now the StructureMap configuration gets even more leaner than it already was.
Jan,
“x.UseDefaultStructureMapConfigFile = false;” is unnecessary if there isn’t a “StructureMap.config” file in the app directory, and…
ForConcreteType()
.Configure
.CtorDependency()
.Is(x => x.TheInstanceNamed(“Processor1”));
Is unnecessary. “Processor1” is the default “IProcessor”, so if you request a ProcessorConsumer1 w/o any prior explicit configuration, StructureMap will use the defaults for all the constructor arguments of ProcessorConsumer.
Nice post,
Jeremy
@Jeremy: Thanks for your input. I was kinda hoping for your feedback ;-).