[vc_custom_heading text=”Strategy Pattern” google_fonts=”font_family:Noto%20Sans%3Aregular%2Citalic%2C700%2C700italic|font_style:400%20regular%3A400%3Anormal”]

The Strategy pattern is one of the simpler Design patterns we might encounter in the world of design patterns. The strategy pattern is also very practical and could help us refactor and clean up some of our existing code.

The basic idea of a strategy pattern is for example when we have some calling code, which doesn’t need to know which strategy we are going to use. It basically can do its job by applying the strategy appropriately without caring at all what strategy is.

Let’s say we have this method:

I am text block. Click edit button to change this text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

[vc_wp_text]

public decimal CalculateOrderDiscount (Customer customer, decimal totalSale) {
  switch (customer.DiscountMethod) {
    case Customer.DiscountOptions.StudentDiscount:
      return ApplyStudentDiscount (customer, totalSale);

    case Customer.DiscountOptions.LoyalDiscount:
      return ApplyLoyalDiscount (customer, totalSale);

    case Customer.DiscountOptions.StudentLoyalDiscount:
      return ApplyStudentLoyalDiscount (customer, totalSale);

    default:
      throw new UnknownDiscountMethodException ("Couldnt find a valid discount Method");

  }

}

decimal ApplyStudentDiscount (Customer order, decimal totalSale) {
  return (decimal) (100 - 15) * totalSale / 100;
}

decimal ApplyLoyalDiscount (Customer order, decimal totalSale) {
  return (decimal) (100 - 10) * totalSale / 100;
}

decimal ApplyStudentLoyalDiscount (Customer order, decimal totalSale) {
  return (decimal) (100 - 30) * totalSale / 100;
}

[/vc_wp_text]

In this method we have a switch statement where we switch on the CalculateOrderDiscount method with possible values StudentDiscount, LoyalDiscount, StudentDiscount.

One thing about using this technique is that is very possible that we’re going to get a new discount passed in on this method that we’re not actually coding for. For that scenario here we are throwing a “UnknownDiscountMethodException”.

Another red flag about this piece of code is that in most cases is not appropriate for the service to know what type of discount the customer fits under.

Let say for example we want to add a Christmas Discount, or National Student Day Discount. In all these cases we are going to have to open up this class and modify the CalculateOrderDiscount method, and that by definition breaks the Open Close Principle right away, which states that a class should be open for extension but closed for modification.

In this case our goal is to be able to calculate things independently of the  CalculateOrderDiscount service, and that’s where the Strategy Pattern comes in.

[vc_gallery interval=”3″ images=”1392″ title=”Strategy Pattern UML Diagram”]

Here our Strategy interface declares a method that is going to be called by DiscountOrderCalculatorService to perform the calculation we’re interested in performing.

[vc_wp_text]

public interface IDiscountStrategy {
  decimal ApplyDiscount (Customer customer, decimal totalSale);
}

[/vc_wp_text]

Then also we have concrete implementations of our Strategy interface for every strategy.

[vc_wp_text]

public class LoyalDiscountStrategy : IDiscountStrategy {
  public decimal ApplyDiscount (Customer customer, decimal totalSale) {
    return (decimal) (100 - 10) * totalSale / 100;
  }
}

public class LoyalStudentDiscountStrategy : IDiscountStrategy {
  public decimal ApplyDiscount (Customer customer, decimal totalSale) {
    return (decimal) (100 - 30) * totalSale / 100;
  }
}

public decimal ApplyDiscount (Customer customer, decimal totalSale) {
  return (decimal) (100 - 15) * totalSale / 100;
}

[/vc_wp_text]

Finally, our Discount Order Calculator takes a strategy in its constructor as a constructor argument, which it holds as a member field to later using that strategy to perform its work.

[vc_wp_text]

public class DiscountOrderCalculatorService {
  private readonly IDiscountStrategy _discountStrategy;

  public DiscountOrderCalculatorService (IDiscountStrategy discountStrategy) {
    _discountStrategy = discountStrategy;
  }

  public decimal ApplyDiscountMethod (Customer customer, int totalSale) {
    return _discountStrategy.ApplyDiscount (customer, totalSale);
  }
}

public class Customer {
  public enum DiscountOptions {
    NoDiscount = 0,
    StudentDiscount = 15,
    LoyalDiscount = 25,
    StudentLoyalDiscount = 30
  }

  public DiscountOptions DiscountMethod { get; set; }
  public int CustomerId { get; set; }
  public string Name { get; set; }
}

[/vc_wp_text][vc_custom_heading text=”Usage:
” font_container=”tag:h5|text_align:left” google_fonts=”font_family:Noto%20Sans%3Aregular%2Citalic%2C700%2C700italic|font_style:400%20regular%3A400%3Anormal”][vc_wp_text]

[Test]
public void When_Apply_Student_Discount_The_Cost_Is_15_Percent_Lest () {
  var strategy = new StudentDiscountStrategy ();

  var discountOrderCalculatorService = new DiscountOrderCalculatorService (strategy);
  var customer = Factory.Create_Customer_With_StudentDiscount ();
  var finalPrice = discountOrderCalculatorService.ApplyDiscountMethod (customer, 100);

  Assert.AreEqual (85, finalPrice);
}

[/vc_wp_text]

Another common way to implement the strategy pattern is using Property Injection,  Func, Actions or Delegates to push in our strategies.

Example:

[vc_wp_text]

[Test]
public void When_Apply_Student_Discount_The_Cost_Is_15_Percent_Lest () {
  var discount = 15; //15%

  Func<Customer, decimal, decimal> studentDiscountStrategy = (c, totalSale) =>
    (decimal) (100 - discount) * totalSale / 100;

  var discountOrderCalculatorService = new DiscountOrderCalculatorService ();
  var customer = Factory.Create_Customer_With_StudentDiscount ();
  var finalPrice =
    discountOrderCalculatorService.ApplyDiscountMethod 
    (customer, 100, studentDiscountStrategy);

  Assert.AreEqual (85, finalPrice);
}

[/vc_wp_text]

[vc_custom_heading text=”Sample Code” font_container=”tag:h2|text_align:left|color:%23e8891e” link=”url:https%3A%2F%2Fgithub.com%2Fcdcalderon%2FStrategyPattern|title:Sample%20Code|target:%20_blank|”]

LEAVE A REPLY

Please enter your comment!
Please enter your name here