Полиморфизм с инъекцией зависимостей с использованием Castle Windsor - PullRequest
4 голосов
/ 22 марта 2011

Как настроить интерфейс, имеющий несколько конкретных реализаций, используя 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 ]

3 голосов
/ 22 марта 2011

Поскольку мы на самом деле не знаем, как вам нужно определить, применяется ли налог на услуги, я бы хотел добавить другое решение к хорошему ответу Марка.Здесь я использую шаблон декоратора:

// 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>())
    )
);
3 голосов
/ 22 марта 2011

Вот один из способов сделать это:

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 после заводского метода, поскольку это позволяет использовать шаблон Decorator в тех случаях, когда выбран ServiceTaxCalculator.

2 голосов
/ 07 ноября 2014

Добавление ответа для демонстрации предпочтения @ 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. Кроме того, это, безусловно, более идиоматично, чем использование фабричного метода для явного создания объекта.

Более подробная информация доступна в документации .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...