시스템 설계| 비즈니스 규칙 구현

조건 논리가 많고 품질이 낮은 일부 레거시 코드를 지원해야 한다고 가정해 보겠습니다.

또한 시스템에서 진행 중인 개발이므로 코드를 이해하고 진행 상황을 소화하기 어려울 수 있으므로 새로운 규칙을 통합하는 것이 상당히 어려울 수 있습니다.

이러한 종류의 코드에는 조건 논리의 다른 부분이 수행하는 작업을 설명하는 주석이 있는 경우가 많습니다.

시간이 지남에 따라 더 많은 조건을 추가해야 하므로 문제는 더욱 악화됩니다.

You can get a better understanding of how the Rules Pattern works by checking out the code in my github repo.



할인 기준 기능이 있는 전자 상거래 상점에 대한 예

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()
  • 를 추가할 수 있습니다.
  • 규칙 요구 사항
  • 의 표현이 있는 BaseRule<T>의 종속성을 사용하는 추상 클래스ICondition에 의해 구현된 IRule
  • 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;
            }
        }
    

    좋은 웹페이지 즐겨찾기