Tuesday, February 09, 2010

10 Advanced Windsor Tricks – 11. Type forwarding

Here’s part eleven of (slightly more than) 10 Advanced Windsor Tricks.

Have you noticed how I keep on putting off the WCF facility? It will come, I’m just waiting until I’ve got the time to do it justice. Instead, today’s trick is a nice easy one: type forwarding. This simply means that you can have a single component satisfy many services. As an example, say we have three interfaces:

public interface IThing
{
    string SayHello(string name);
}

public interface IWidget
{
    double Calculate(double a, double b);
}

public interface IWonder
{
    void DoesNothing();
}

Here’s a class called SrpViolator ;) It implements all three interfaces:

public class SrpViolator : IThing, IWidget, IWonder
{
    public string SayHello(string name)
    {
        return string.Format("Hello {0} from SrpViolator", name);
    }

    public double Calculate(double a, double b)
    {
        return Math.Pow(a, b);
    }

    public void DoesNothing()
    {
        Console.WriteLine("Doing nothing");
    }
}

Type forwarding allows us to register all three interfaces as satisfied by SrpViolator:

var container = new WindsorContainer()
    .Register(
        Component
            .For<IThing>()
            .Forward<IWidget>()
            .Forward<IWonder>()
            .ImplementedBy<SrpViolator>()
    );

Update: Krzysztof Kozmic has pointed out that you can also simply have a comma separated list of interfaces in the ‘For’ clause for exactly the same effect:

var container = new WindsorContainer()
    .Register(
        Component
            .For<IThing, IWidget, IWonder>()
            .ImplementedBy<SrpViolator>()
    );

Now we can resolve each interface independently, but behind the scenes they are all share the same implementation:

var thing = container.Resolve<IThing>();
Console.WriteLine(thing.SayHello("Krzysztof"));

var widget = container.Resolve<IWidget>();
Console.WriteLine("The answer is {0}", widget.Calculate(2, 3));

var wonder = container.Resolve<IWonder>();
wonder.DoesNothing();

Which outputs:

Hello Krzysztof from SrpViolator
The answer is 8
Doing nothing

Since SrpViolator is registered without specifying a lifestyle, it will have the default lifestyle: singleton. That means that ‘thing’, ‘widget’ and ‘wonder’ from the code snippet above are all the same instance of SrpViolator.

Type forwarding can be very useful, but note that the Single Responsibility Principle means that it’s generally considered bad practice to have a single class play many different roles.

No comments: