An interface / virtual-dispatch approach might look something like this:
// First we loosely define how a "discount" can be used.
// This could also be an abstract class, if common base-class
// functionality is desired.
public interface IDiscount
{
// This is called to apply this discount to a particular product
void ApplyDiscount(Product product);
}
// Here's one implementation that applies a percentage discount
public class PercentDiscount : IDiscount
{
private decimal m_percent;
public PercentDiscount(decimal percent) {
m_percent = percent;
}
#region IDiscount implementation
public void ApplyDiscount(Product product) {
product.Price -= product.Price * m_discount;
}
#endregion
}
// Here's another implementation that makes a product free
public class FreeDiscount : IDiscount
{
public FreeDiscount() {
}
#region IDiscount implementation
public void ApplyDiscount(Product product) {
product.Price = 0;
}
#endregion
}
public class SomeClass {
// Now applying the discounts becomes much simpler! Note that this function
// takes a collection of IDiscounts, and applies them in a consistent way,
// by just calling IDiscount.ApplyDiscount()
void ApplyDiscounts(IEnumerable<IDiscount> discounts, Product product) {
foreach (var discount in discounts) {
discount.ApplyDiscount(product);
}
}
}
Note that I also changed ApplyDiscounts
to take an IEnumerable<T>
instead of List<T>
. This allows any arbitrary collection type to be passed, and also doesn't allow the function to inadvertently modify the collection.