Введите звонок автоматически - PullRequest
0 голосов
/ 01 июля 2018

У меня есть сценарий, который основан на принципе Open Closed. У меня есть Cart класс, который имеет метод CalculateTotal(). Этот метод принимает MODE и QTY в качестве параметров.

На основе MODE рассчитывается сумма.

public class Cart
{
    ICalculator calculator;
    public Cart()
    {
        calculator = new Calculator();
    }
    public decimal CalculateTotal(string MODE,decimal QTY)
    {
        return calculator.CalculateTotal(MODE, QTY);
    }
}

public interface ICalculator
{
    decimal CalculateTotal(string MODE, decimal QTY);
}

public class Calculator : ICalculator
{
    private readonly List<IRule> rules;

    public Calculator()
    {
        rules = new List<IRule>();
        rules.Add(new OrdinaryRule());
        rules.Add(new SpecialRule());
        rules.Add(new OfferRule());
    }

    public decimal CalculateTotal(string MODE, decimal QTY)
    {
        return rules.First(x => x.IsMatch(MODE)).CalculateTotal(QTY);
    }
}

public interface IRule
{
    bool IsMatch(string MODE);
    decimal CalculateTotal(decimal QTY);
}

public class OrdinaryRule : IRule
{
    public decimal CalculateTotal(decimal QTY)
    {
        return QTY * 2m;
    }

    public bool IsMatch(string MODE)
    {
        return MODE.StartsWith("ORD");
    }
}

Если мне нужно добавить новое правило, скажем FestivalRule, тогда я могу реализовать интерфейс, создать новое правило и добавить это правило в конструкторе Calculator().

Тем не менее я чувствую, что я изменяю класс Calculator.

Есть ли способ, которым мне не нужно добавлять / изменять класс Calculator, и все же применяется новое правило?

1 Ответ

0 голосов
/ 01 июля 2018

Текущий дизайн все еще тесно связывает ваши классы с проблемами реализации.

Используя шаблон стратегии и принцип явной зависимости, введите зависимости.

public class Cart {
    private readonly ICalculator calculator;

    public Cart(ICalculator calculator) {
        this.calculator = calculator;
    }

    public decimal CalculateTotal(string MODE, decimal QTY) {
        return calculator.CalculateTotal(MODE, QTY);
    }
}

public class Calculator : ICalculator {
    private readonly List<IRule> rules;

    public Calculator(IEnumerable<IRule> rules) {
        this.rules = new List<IRule>(rules);
    }

    public decimal CalculateTotal(string MODE, decimal QTY) {
        return rules.First(x => x.IsMatch(MODE)).CalculateTotal(QTY);
    }
}

Так что теперь, используя чистый DI или контейнер, эти классы не связаны с проблемами реализации, что позволяет расширять их поведение без изменения их исходного кода (OCP).

List<IRule> rules = new List<IRule>();
rules.Add(new OrdinaryRule());
rules.Add(new SpecialRule());
rules.Add(new OfferRule());
rules.Add(new FestivalRule()); //<<<

var calculator = new Calculator(rules);

var cart = new Cart(calculator);

//...

Контейнер DI будет управлять тяжелой работой и автоматическим внедрением зависимостей при разрешении графа объекта. При условии, что он был настроен соответствующим образом.

...