Here’s part four of (at least) 10 Advanced Windsor Tricks.
The decorator pattern is an old friend from the Gang of Four book and goes back to the earliest days of Object Oriented programming. It’s a very simple but powerful idea. Let’s remember our old friend ThingOne:
public class ThingOne : IThing { public string SayHello(string name) { return string.Format("ThingOne says hello to {0}", name); } }
A decorator is a class that both implements an interface and has a reference to it. This is so that it can pretend to be something else and intercept any calls made to the wrapped instance. Here’s an IThing decorator:
public class ThingDecorator : IThing { private readonly IThing thing; public ThingDecorator(IThing thing) { this.thing = thing; } public string SayHello(string name) { Console.WriteLine("Before calling wrapped IThing"); var message = thing.SayHello(name); Console.WriteLine("After calling wrapped IThing"); return message; } }
A decorator can do anything it likes before and after the call to the wrapped class including altering its arguments and/or return value. There is much discussion of AOP these days and heavyweight frameworks like PostSharp; indeed Windsor itself is an excellent AOP framework; but often a decorator is a much simpler solution to these kinds of interception requirements. Don’t get me wrong though, I think PostSharp is the bees knees :)
Now we can compose the decorator with the target and allow the decorator to intercept calls to the target:
var thing = new ThingDecorator(new ThingOne()); Console.WriteLine(thing.SayHello("Roger"));
Which will print out:
Before calling wrapped IThing After calling wrapped IThing ThingOne says hello to Roger
Windsor makes working with decorators a breeze. You don’t have to explicitly wire up the decorator with a service override, simply order the registrations with the outermost decorator first and Windsor will magically wire them up for you. So to register ThingDecorator and ThingOne just do the following:
var container = new WindsorContainer() .Register( Component.For<IThing>().ImplementedBy<ThingDecorator>(), Component.For<IThing>().ImplementedBy<ThingOne>() ); var thing = container.Resolve<IThing>(); Console.WriteLine(thing.SayHello("Roger"));
This prints out the same result as the code above.
Now, let’s introduce a second decorator:
public class ThingDecorator2 : IThing { private readonly IThing thing; public ThingDecorator2(IThing thing) { this.thing = thing; } public string SayHello(string name) { return thing.SayHello(name + " has been altered by ThingDecorator2... ha ha ha!"); } }
When we register this decorator as well we get just the effect we expect:
public void decorator_should_work_without_any_special_registration() { var container = new WindsorContainer() .Register( Component.For<IThing>().ImplementedBy<ThingDecorator>(), Component.For<IThing>().ImplementedBy<ThingDecorator2>(), Component.For<IThing>().ImplementedBy<ThingOne>() ); var thing = container.Resolve<IThing>(); Console.WriteLine(thing.SayHello("Roger")); }
This prints out:
Before calling wrapped IThing After calling wrapped IThing ThingOne says hello to Roger has been altered by ThingDecorator2... ha ha ha!
It’s nice, in fact it’s cooler than a Shaft and Fonz love child :)
Instead of doing the following to configure the decorators:
ReplyDeletevar container = new WindsorContainer()
.Register(
Component.For().ImplementedBy(),
Component.For().ImplementedBy(),
Component.For().ImplementedBy()
);
Can I create the same type of chain in windsor's XML configuration so I can add and remove decorators by simply changing the config file?
thanks
KnightRider, yes you can :)
ReplyDeleteAny direction/link you can provide to show how to do it in config?
ReplyDeleteI plan to use caching and logging between my UI and service layer and between my service layer and repository and would like to easily be able to plug them in and pull them out via config.
thanks
This is the equivalent of the fluent registration above:
ReplyDelete<configuration>
<components>
<component service="My.Project.IThing" type="My.Project.ThingDecorator" />
<component service="My.Project.IThing" type="My.Project.ThingDecorator2" />
<component service="My.Project.IThing" type="My.Project.ThingOne" />
</components>
</configuration>
Ok, so it just builds the decorator wrappers in the order they appear in the config file.
ReplyDeleteSo ThingOne is wrapped by ThingDecorator2 which is wrapped by ThingDecorator. So what I call ServiceLocator.GetService[IThing]), I'll get back an object who's "outermost" object is a ThingDecorator.
Thanks Mike! Previously I was using named services and calling them in the order I wanted by name, and that was smelly, sticky, and all kinds of bad. :)
I've got a storedProcedureSelect decorating storedProcedureBase and storedProcedureUpdate decorating storedProcedureBase aswell but I do not want storedProcedureUpdate to wrap storedProcedureSelect. How do I do two decorator registrations that are independent from each other?
ReplyDeleteThere's good reason against mixing component registration and decorator configuration. Service overrides define which imlementation is used by a component that needs it. Registration order defines a default implementation for a service, which is a rather different concern. A working example, though.
ReplyDeleteHi I want to resolve my interface automatically with out calling that resolve method.
ReplyDeleteIs there any way to do that ?
Please help me....
very nice and funny ;)
ReplyDelete