Polymorphism with Dependency Injection using Castle Windsor
Asked Answered
P

3

5

How to configure Interface having multiple concrete implementation using Castle Windsor (using code). Below is the sample code.

public interface ICostCalculator
{
    double CalculateTotal(Order order);
}

public class DefaultCostCalculator : ICostCalculator
{
    public double CalculateTotal(Order order)
    {
        return
            order.Items.Sum(x => x.Product.Rate * x.Quantity);
    }
}

The ServiceTaxCalculator implementation:

public class ServiceTaxCalculator : ICostCalculator
{
    private readonly ICostCalculator calculator;
    private double serviveTaxRate = 10.2;

    public ServiceTaxCalculator(ICostCalculator calculator)
    {
        this.calculator = calculator;
    }

    public double ServiceTaxRate
    {
        get { return this.serviceTaxRate; }
        set { this.serviceTaxRate = value; }
    }

    public double CalculateTotal(Order order)
    {
        double innerTotal = 
            this.calculator.CalculateTotal(order);
        innerTotal += innerTotal * servieTaxRate / 100;
        return innerTotal;
    }
}

I want the instance of a concrete class based on service tax applicability. If service tax is applicable, I need ServiceTaxCalculator else DefaultCostCalculator.

How to configure this scenario using Castle Windsor.

Patriliny answered 22/3, 2011 at 12:14 Comment(1)
And how do you know when the service tax is applicable?Conventicle
C
3

Since we don't really know how you need to determine whether the service tax is applicable, I like to add another solution to Mark's nice answer. Here I use the decorator pattern:

// Decorator
public class ServiceTaxApplicableCostCalculator 
    : ICostCalculator
{
    private readonly ICostCalculator with;
    private readonly ICostCalculator without

    ServiceTaxApplicableCostCalculator(
        ICostCalculator with, ICostCalculator without)
    {
        this.with = with;
        this.without = without;
    }

    public double CalculateTotal(Order order)
    {
        bool withTax = this.IsWithTax(order);

        var calculator = withTax ? this.with : this.without;

        return calculator.CalculateTotal(order);
    }

    private bool IsWithTax(Order order)
    {
        // determine if the order is with or without tax.
        // Perhaps by using a config setting or by querying
        // the database.
    }
}

Now you can register this decorator:

container.Register(Component.For<ServiceTaxCalculator>());
container.Register(
    Component.For<DefaultCostCalculator, ICostCalculator>());

container.Register(Component.For<ICostCalculator>()
    .UsingFactoryMethod(k => 
        new ServiceTaxApplicableCostCalculator(
            k.Resolve<ServiceTaxCalculator>(),
            k.Resolve<DefaultCostCalculator>())
    )
);
Conventicle answered 22/3, 2011 at 13:4 Comment(4)
That's not going to compile as is. The ServiceTaxCalculator constructor requires an instance of ICostCalculator. In general, it's better to use k.Resolve<Foo>() than new Foo() from within UsingFactoryMethod, as this will respect other component registration aspects, such as lifetime configuration. By using new you totally override those concerns.Resile
@Mark: Fair enough: fixed it.Conventicle
+1 This points the way to a general solution to this sort of problem when in those cases where the container doesn't support conditional resolution. In those cases you can define an even more specialized 'branching' implementation as an infrastructure component and register it without registering the other implementations. However, this is not necessary for Castle Windsor.Resile
Use service overrides instead of inline constructor call. Windsor is good at creating objects, let it handle that part.Seedtime
R
5

Here's one way to do it:

container.Register(Component
    .For<ICostCalculator>()
    .UsingFactoryMethod(k => 
        isServiceTaxApplicable ? 
        (ICostCalculator)k.Resolve<ServiceTaxCalculator>() : 
        k.Resolve<DefaultCostCalculator>()));
container.Register(Component.For<DefaultCostCalculator, ICostCalculator>());
container.Register(Component.For<ServiceTaxCalculator>());

Notice that the isServiceTaxApplicable variable in this example is an outer variable (not shown), but you can easily replace it with some other boolean check.

Also notice that the DefaultCostCalculator forwards the registration to the ICostCalculcator interface. However, since this is not the first registration of that interface, it's not the default registration.

It's important to register the DefaultCostCalculator after the factory method because this enables the Decorator pattern in those cases where the ServiceTaxCalculator is chosen.

Resile answered 22/3, 2011 at 12:47 Comment(2)
Use service overrides instead. or IHandlerSelector depending where isServiceTaxApplicable comes from.Seedtime
I don't see how service overrides would work here... Why do you prefer that instead?Resile
C
3

Since we don't really know how you need to determine whether the service tax is applicable, I like to add another solution to Mark's nice answer. Here I use the decorator pattern:

// Decorator
public class ServiceTaxApplicableCostCalculator 
    : ICostCalculator
{
    private readonly ICostCalculator with;
    private readonly ICostCalculator without

    ServiceTaxApplicableCostCalculator(
        ICostCalculator with, ICostCalculator without)
    {
        this.with = with;
        this.without = without;
    }

    public double CalculateTotal(Order order)
    {
        bool withTax = this.IsWithTax(order);

        var calculator = withTax ? this.with : this.without;

        return calculator.CalculateTotal(order);
    }

    private bool IsWithTax(Order order)
    {
        // determine if the order is with or without tax.
        // Perhaps by using a config setting or by querying
        // the database.
    }
}

Now you can register this decorator:

container.Register(Component.For<ServiceTaxCalculator>());
container.Register(
    Component.For<DefaultCostCalculator, ICostCalculator>());

container.Register(Component.For<ICostCalculator>()
    .UsingFactoryMethod(k => 
        new ServiceTaxApplicableCostCalculator(
            k.Resolve<ServiceTaxCalculator>(),
            k.Resolve<DefaultCostCalculator>())
    )
);
Conventicle answered 22/3, 2011 at 13:4 Comment(4)
That's not going to compile as is. The ServiceTaxCalculator constructor requires an instance of ICostCalculator. In general, it's better to use k.Resolve<Foo>() than new Foo() from within UsingFactoryMethod, as this will respect other component registration aspects, such as lifetime configuration. By using new you totally override those concerns.Resile
@Mark: Fair enough: fixed it.Conventicle
+1 This points the way to a general solution to this sort of problem when in those cases where the container doesn't support conditional resolution. In those cases you can define an even more specialized 'branching' implementation as an infrastructure component and register it without registering the other implementations. However, this is not necessary for Castle Windsor.Resile
Use service overrides instead of inline constructor call. Windsor is good at creating objects, let it handle that part.Seedtime
K
2

Adding an answer to demonstrate @Kryzsztof's preference for service overrides. Instead of a factory method:

container.Register(Component.For<ICostCalculator>()
    .UsingFactoryMethod(k => 
        new ServiceTaxApplicableCostCalculator(
            k.Resolve<ServiceTaxCalculator>(),
            k.Resolve<DefaultCostCalculator>())
    )
);

You would instead specify the dependencies via DependsOn:

container.Register(Component.For<ICostCalculator>()
    .ImplementedBy<ServiceTaxApplicableCostCalculator>()
    .DependsOn(Dependency.OnComponent("with", typeof(ServiceTaxCalculator)))
    .DependsOn(Dependency.OnComponent("without", typeof(DefaultCostCalculator))));

The only benefit that is obvious to me is that if a different service is added to ServiceTaxApplicableCostCalculator's constructor, the service override case would continue to work without any changes (resolving the new service automatically), whereas the factory method would require another call to Resolve. Beyond that, it is certainly more idiomatic than using a factory method to explicitly create an object.

More information is available in the documentation.

Krystynakshatriya answered 7/11, 2014 at 16:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.