Проектирование системы| Реализация бизнес-правил



Предположим, что нам приходится поддерживать старый код, в котором много условной логики и низкое качество.

Кроме того, система постоянно развивается, поэтому интегрировать новые правила может быть довольно сложно, так как код может быть трудно понять и понять, что происходит.

Такой код часто содержит комментарии, объясняющие, что делают различные части условной логики.

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

Вы можете лучше понять, как работает паттерн «Правила», просмотрев код в моем репозитории на github.

В примере мы имеем дело с магазином электронной коммерции, который имеет функцию критериев скидок

public class DiscountCalculator
{
    public decimal CalculateDiscountPercentage(Customer customer)
    {
        decimal discount = 0;
        if (customer.DateOfBirth < DateTime.Now.AddYears(-65))
        {
            // senior discount 5%
            discount = .05m;
        }

        if (customer.DateOfBirth.Day == DateTime.Today.Day &&
            customer.DateOfBirth.Month == DateTime.Today.Month)
        {
            // birthday 10%
            discount = Math.Max(discount, .10m);
        }

        if (customer.DateOfFirstPurchase.HasValue)
        {
            if (customer.DateOfFirstPurchase.Value < DateTime.Now.AddYears(-1))
            {
                // after 1 year, loyal customers get 10%
                discount = Math.Max(discount, .10m);
                if (customer.DateOfFirstPurchase.Value < DateTime.Now.AddYears(-5))
                {
                    // after 5 years, 12%
                    discount = Math.Max(discount, .12m);
                    if (customer.DateOfFirstPurchase.Value < DateTime.Now.AddYears(-10))
                    {
                        // after 10 years, 20%
                        discount = Math.Max(discount, .2m);
                    }
                }

                if (customer.DateOfBirth.Day == DateTime.Today.Day &&
                    customer.DateOfBirth.Month == DateTime.Today.Month)
                {
                    // birthday additional 10%
                    discount += .10m;
                }
            }
        }
        else
        {
            // first time purchase discount of 15%
            discount = Math.Max(discount, .15m);
        }
        return discount;
    }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Паттерн проектирования правил
тот факт, что сложность будет увеличиваться по мере добавления новых правил.

Я искал шаблон проектирования, который мог бы решить эти проблемы, и наткнулся на шаблон правил.

Паттерн правил работает путем отделения правил от логики обработки правил, применяя принцип единой ответственности.

Это позволяет легко добавлять новые правила без изменения остальной части системы, применяя принцип открытости/закрытости.

изображение нового дизайна

иллюстрация к приведенному выше изображению:

  • IRule : содержит все методы потока правил, такие как ClearConditions() , Initialize() , IsValid() , Apply() , OnRuleViolated(), а также можно добавить OnRuleCompleted().

  • IRule реализован абстрактным классом BaseRule<T>, который использует зависимость ICondition, содержащую выражение требования правила.

  • RulesEngine.cs имеет ApplyRule(), который указывает путь выполнения правила

  • сами классы правил наследуют базовое правило и переопределяют тело метода

Пример BirthdayDiscountRule:

public class BirthdayDiscountRule : BaseRule<Customer>
    {
        public override void Initialize(Customer obj)
        {
            Conditions.Add(new ConditionExtension(CustomerExtensions.IsBirthday(obj)));
        }

        public override Customer Apply(Customer obj)
        {
            obj.Discount += .10m;
            return obj;
        }
        public override void OnRuleViolated(Customer obj)
        {
            Console.WriteLine($"{this.GetType().Name}  Violated");
        }
    }
Вход в полноэкранный режим Выход из полноэкранного режима

вот фрагмент кода конечного результата

class Program
    {
        static void Main(string[] args)
        {
            var productA = new Product { Price = 100, Name = "Apple Watch" };
            var productB = new Product { Price = 50, Name = "Samsung Watch" };

            var customer = new Customer
            {
                Products = new List<Product> { productA, productB, productA, productA },
                DateOfFirstPurchase = DateTime.Now.AddYears(-2),
                DateOfBirth = DateTime.Now.AddYears(-5),
            };
            customer.TotalValue = customer.Products.Sum(p => p.Price);

            var ruleManager = new CustomerRuleManager(customer);
            var result = ruleManager.Run();
        }
    }
    public class CustomerRuleManager
    {
        private readonly Customer _customer;

        public CustomerRuleManager(Customer customer)
        {
            _customer = customer;
        }

        public Customer Run()
        {
            _customer
                .ApplyRule(new BirthdayDiscountRule())
                .ApplyRule(new BuyThreeGetThirdFree("Apple Watch"))
                .ApplyRule(new FreeShipping())
                .ApplyRule(new LoyalCustomerRule(5, 0.12m))
                .ApplyRule(new NewCustomerRule())
                .ApplyRule(new RetiredDiscountRule());

            return _customer;
        }
    }
Вход в полноэкранный режим Выход из полноэкранного режима

Оцените статью
devanswers.ru
Добавить комментарий