【问题标题】:Polymorphism with Dependency Injection using Castle Windsor使用 Castle Windsor 进行依赖注入的多态性
【发布时间】:2011-03-22 12:14:14
【问题描述】:

如何使用 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 配置此场景。

【问题讨论】:

  • 你怎么知道服务税什么时候适用?

标签: c# dependency-injection castle-windsor


【解决方案1】:

这是一种方法:

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 的情况下启用装饰器模式。

【讨论】:

  • 改用服务覆盖。或IHandlerSelector 取决于isServiceTaxApplicable 的来源。
  • 我看不到服务覆盖在这里是如何工作的......为什么你更喜欢它?
【解决方案2】:

由于我们真的不知道您需要如何确定服务税是否适用,我想为 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>())
    )
);

【讨论】:

  • 这不会按原样编译。 ServiceTaxCalculator 构造函数需要一个ICostCalculator 实例。一般来说,在 UsingFactoryMethod 中使用 k.Resolve&lt;Foo&gt;() 比使用 new Foo() 更好,因为这将尊重其他组件注册方面,例如生命周期配置。通过使用new,您可以完全忽略这些顾虑。
  • @Mark:很公平:修好了。
  • +1 在容器不支持条件解析的情况下,这为此类问题的一般解决方案指明了方向。在这些情况下,您可以将更专业的“分支”实现定义为基础架构组件,并在不注册其他实现的情况下注册它。但是,对于温莎城堡来说,这不是必需的。
  • 使用服务覆盖而不是内联构造函数调用。温莎擅长创造物体,让它自己处理。
【解决方案3】:

添加一个答案来证明@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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2011-04-24
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多