Tuesday, January 18, 2011

Strategy Pattern

What

Encapsulate a family of algorithms or behaviors into a set of interchangeable classes. The algorithm/behavior classes are made interchangeable via concrete implementations against a common algorithm/behavior interface.

Why

The algorithm/strategy classes can be modified independent of the classes that use them as long as they adhere to the original interface they implemented.
Code duplication is minimized because the same algorithm or behavior does not need to be implemented in each individual class that needs to have that algorithm/behavior.
The algorithm/behavior of the classes can be dynamically set at runtime via constructor injection or a setter property.

How

Say we need to apply a promotional discount to the total invoice amount. The promotional discount can vary based on the marketing campaign in effect.
interface IPromoDiscount
{
  decimal ApplyDiscount(decimal amount);
}
// The logic to apply discount is encapsulated here
class PromoDiscount : IPromoDiscount
{
  decimal m_DiscountRate;
 
  private PromoDiscount() {}
 
  public PromoDiscount(decimal discountRate)
  {
    this.m_DiscountRate = discountRate;
  }
 
  public decimal ApplyDiscount(decimal amount)
  {
    return (amount * (1 - m_DiscountRate));
  }
}

class Invoice
{
  // invoice does not know about the real implementation
  // of IPromoDiscount
  IPromoDiscount m_PromoDiscount;
 
  private Invoice() {}
 
  // Injecting the 'algorithm' to calculate discount
  public Invoice(IPromoDiscount promoDiscount)
  {
    this.m_PromoDiscount = promoDiscount;
  }
  private decimal CalculateSumOfAllInvoiceLines()
  {
    ...
  }
 
  public decimal InvoiceTotal
  {
    get
    {
       return m_PromoDiscount.ApplyDiscount(CalculateSumOfAllInvoiceLines());
    }
  }
}
// Specify the discount to apply for an Invoice
// The same Invoice class can be used without modification
// for all three discount scenarios
Invoice invoiceNoDiscount = new Invoice(new PromoDiscount(0M));
Invoice invoiceWithTenPercentDiscount = new Invoice(new PromoDiscount(0.10M));
Invoice invoiceWithTwentyPercentDiscount = new Invoice(new PromoDiscount(0.20M));