使用Castle Windsor进行依赖注入的多态性

时间:2011-03-22 12:14:15

标签: c# dependency-injection castle-windsor

如何使用Castle Windsor(使用代码)配置具有多个具体实现的接口。以下是示例代码。

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);
    }
}

ServiceTaxCalculator实施:

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;
    }
}

我想要一个基于服务税适用性的具体类的实例。如果服务税适用,我需要ServiceTaxCalculator其他DefaultCostCalculator

如何使用Castle Windsor配置此方案。

3 个答案:

答案 0 :(得分:3)

这是一种方法:

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>());

请注意,此示例中的isServiceTaxApplicable变量是外部变量(未显示),但您可以使用其他布尔检查轻松替换它。

另请注意,DefaultCostCalculator将注册转发到ICostCalculcator接口。但是,由于这不是该界面的首次注册,因此它不是默认注册。

在工厂方法之后注册DefaultCostCalculator 非常重要,因为这样可以在选择ServiceTaxCalculator的情况下启用Decorator模式。

答案 1 :(得分:3)

由于我们不知道您需要如何确定服务税是否适用,我想为Mark的好答案添加另一个解决方案。这里我使用装饰器模式:

// 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.
    }
}

现在你可以注册这个装饰:

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>())
    )
);

答案 2 :(得分:2)

添加答案以证明@ Kryzsztof对服务覆盖的偏好。而不是工厂方法:

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

您可以通过DependsOn指定依赖项:

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

对我来说唯一明显的好处是,如果向ServiceTaxApplicableCostCalculator的构造函数添加了不同的服务,则服务覆盖情况将继续工作而不进行任何更改(自动解析新服务),而工厂方法需要再次调用Resolve。除此之外,它肯定比使用工厂方法显式创建对象更具惯用性。

documentation中提供了更多信息。