- It works by separating out the rules from the rules processing logic.
- This makes it easy to add new rules without changing the rest of the system.
- With the Rules Pattern there is an
Evaluator
class that loops through a collection of rules and executes them.
- It evaluates the result and decides what action to take.
- In the simplest case, it just executes all the rule.
- It is also possible to add some selection logic to each rule that allows the
Evaluator
class to decide whether or not to run the rule.
- Applying the refactoring to the problems, the
Evaluator
will have a collection of rules that calculate rules.
public class Customer
{
public string Name { get; set; }
public DateTime BirthDay { get; set; }
public bool HasBeenLoyalForYears(int yearsAsCustomer, DateTime date) => true;
public bool IsBirthday() => BirthDay.Date == DateTime.Now.Date;
}
public interface IDiscountRule
{
decimal CalculateCustomerDiscount(Customer customer);
}
public class BirthdayDiscountRule : IDiscountRule
{
public decimal CalculateCustomerDiscount(Customer customer)
{
return customer.IsBirthday() ? 0.10m : 0;
}
}
public class LoyalCustomerRule : IDiscountRule
{
private readonly int _yearsAsCustomer;
private readonly decimal _discount;
private readonly DateTime _date;
public LoyalCustomerRule(int yearsAsCustomer, decimal discount, DateTime? date = null)
{
_yearsAsCustomer = yearsAsCustomer;
_discount = discount;
_date = date ?? DateTime.Now;
}
public decimal CalculateCustomerDiscount(Customer customer)
{
if (customer.HasBeenLoyalForYears(_yearsAsCustomer, _date))
{
var birthdayRule = new BirthdayDiscountRule();
return _discount + birthdayRule.CalculateCustomerDiscount(customer);
}
return 0;
}
}
public class RulesDiscountCalculator
{
List<IDiscountRule> _rules = new List<IDiscountRule>();
public RulesDiscountCalculator()
{
_rules.Add(new BirthdayDiscountRule());
_rules.Add(new LoyalCustomerRule(1, 0.10m));
_rules.Add(new LoyalCustomerRule(5, 0.12m));
_rules.Add(new LoyalCustomerRule(10, 0.20m));
}
public decimal CalculateDiscountPercentage(Customer customer)
{
decimal discount = 0;
foreach (var rule in _rules)
discount = Math.Max(rule.CalculateCustomerDiscount(customer), discount);
return discount;
}
}