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

Nir*_*ngh 5 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

如何使用 Castle Windsor 配置此场景。

Mar*_*ann 5

这是一种方法:

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>());
Run Code Online (Sandbox Code Playgroud)

请注意,isServiceTaxApplicable此示例中的变量是一个外部变量(未显示),但您可以轻松地将其替换为其他一些布尔检查。

还要注意 DefaultCostCalculator 将注册转发到 ICostCalculator 接口。但是,由于这不是该接口的第一次注册,因此它不是默认注册。

在工厂方法之后注册 DefaultCostCalculator 很重要,因为这会在选择 ServiceTaxCalculator 的情况下启用装饰器模式。


Ste*_*ven 3

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

// 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.
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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>())
    )
);
Run Code Online (Sandbox Code Playgroud)

  • +1 当容器不支持条件解析的情况下,这指出了此类问题的通用解决方案。在这些情况下,您可以将更专业的“分支”实现定义为基础架构组件并注册它,而无需注册其他实现。然而,这对于温莎城堡来说并不是必需的。 (3认同)
  • 这不会按原样编译。ServiceTaxCalculator 构造函数需要 ICostCalculator 的实例。一般来说,在UsingFactoryMethod中使用`k.Resolve&lt;Foo&gt;()`比使用`new Foo()`更好,因为这将尊重其他组件注册方面,例如生命周期配置。通过使用“new”,您可以完全克服这些担忧。 (2认同)