Пример - правила расчета скидки для позиции заказа:
public class OrderItem
{
public int ItemId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal Total => Quantity * Price;
}
Договор (он же интерфейс) на скидку можно определить так:
public interface IDiscountRule
{
decimal CalculateDiscount(OrderItem item);
}
Теперь давайте реализуем некоторые правила скидок
- Базовая скидка (2%)
- Серебряная скидка (5%)
- Золотая скидка (10%)
Определения классов:
/// <summary>
/// Abstract base class for the discount rules
/// </summary>
public abstract class DiscountRule : IDiscountRule
{
private readonly int _percent;
protected DiscountRule(int percent)
{
_percent = percent;
}
/// <inheritdoc />
public decimal CalculateDiscount(OrderItem item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
decimal discount = item.Total * (decimal) (_percent/100.0);
return discount;
}
}
/// <summary>
/// 2% discount
/// </summary>
public class BasicDiscountRule : DiscountRule
{
public BasicDiscountRule() : base(2)
{
}
}
/// <summary>
/// 5% discount
/// </summary>
public class SilverDiscountRule : DiscountRule
{
public SilverDiscountRule() : base(5)
{
}
}
/// <summary>
/// 10% discount
/// </summary>
public class GoldDiscountRule : DiscountRule
{
public GoldDiscountRule() : base(10)
{
}
}
Расчет скидки для позиции заказа с использованием правила:
var item = new OrderItem
{
ItemId = 1,
Price = 50,
Quantity = 2
};
var rule = new SilverDiscountRule();
decimal discount = rule.CalculateDiscount(item);
Теперь давайте создадим составное правило, чтобы применить несколько правил (скидок) к позиции заказа:
/// <summary>
/// To apply multiple discount rules to an order item
/// </summary>
public class CompositeDiscountRule : IDiscountRule
{
private readonly List<Type> _discountTypes;
public CompositeDiscountRule()
{
_discountTypes = new List<Type>();
}
public CompositeDiscountRule(List<Type> discountTypes)
{
if (discountTypes == null)
{
throw new ArgumentNullException(nameof(discountTypes));
}
_discountTypes = discountTypes;
}
public void Register<T>() where T : IDiscountRule, new()
{
_discountTypes.Add(typeof(T));
}
/// <inheritdoc />
public decimal CalculateDiscount(OrderItem item)
{
decimal totalDiscount = 0;
foreach (var discountType in _discountTypes)
{
IDiscountRule rule = Activator.CreateInstance(discountType) as IDiscountRule;
if (rule != null)
{
totalDiscount += rule.CalculateDiscount(item);
}
}
return totalDiscount;
}
}
Вы можете использовать составное правило со списком типов:
var item = new OrderItem
{
ItemId = 1,
Price = 50,
Quantity = 2
};
// composite rule with types
var compositeRule = new CompositeDiscountRule(new List<Type>
{
typeof(GoldDiscountRule),
typeof(BasicDiscountRule)
});
decimal discount = compositeRule.CalculateDiscount(item); // discount = 12.0
Или, в качестве альтернативы, используя дженерики:
var item = new OrderItem
{
ItemId = 1,
Price = 50,
Quantity = 2
};
// composite rule using generics
compositeRule = new CompositeDiscountRule();
compositeRule.Register<BasicDiscountRule>();
compositeRule.Register<SilverDiscountRule>();
decimal discount = compositeRule.CalculateDiscount(item); // discount = 7.0