Last active
July 4, 2018 10:34
-
-
Save karbyninc/48fff4ed97491bc2b9d4 to your computer and use it in GitHub Desktop.
The Strategy Pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Contracts | |
{ | |
public interface IDiscountStrategy | |
{ | |
decimal ApplyDiscount(decimal price); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Strategy | |
{ | |
//Apply a 20% discount when buying in bulk. | |
public class GroupDiscountStrategy : IDiscountStrategy | |
{ | |
public decimal ApplyDiscount(decimal price) | |
{ | |
//Applies a 20% Group Discount | |
return price * 0.80M; | |
} | |
} | |
} | |
//and in another class, add: | |
namespace StrategyPattern.Strategy | |
{ | |
public class NullDiscountStrategy : IDiscountStrategy | |
{ | |
public decimal ApplyDiscount(decimal price) | |
{ | |
return price; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Models | |
{ | |
public class Event | |
{ | |
public int ID { get; set; } | |
public Price TicketPrice { get; set; } | |
public string Name { get; set; } | |
public Location Location { get; set; } | |
public DateTime Date { get; set; } | |
} | |
public class Location | |
{ | |
public string Name { get; set; } | |
public string Address1 { get; set; } | |
public string Address2 { get; set; } | |
public string State { get; set; } | |
public string Zipcode { get; set; } | |
} | |
} | |
//and in another class: | |
namespace StrategyPattern.Models | |
{ | |
public class Price | |
{ | |
IDiscountStrategy _discountStrategy; | |
public decimal Cost { get; set; } | |
public decimal DiscountedCost { get; set; } | |
/// | |
/// When you instantiate a price class, it defaults to doing nothing (returning the same cost). | |
/// | |
public Price() | |
{ | |
_discountStrategy = new NullDiscountStrategy(); | |
} | |
/// | |
/// Swaps out and recalculates the cost of a given Price through the applied discount algorithm | |
/// | |
///public void SetAndApplyDiscountStrategy(IDiscountStrategy discountStrategy) | |
{ | |
_discountStrategy = discountStrategy; | |
this.DiscountedCost = _discountStrategy.ApplyDiscount(this.Cost); | |
} | |
} | |
} | |
//and finally our repository: | |
namespace StrategyPattern.Repositories | |
{ | |
public class FakeEventRepository : IEventRepository | |
{ | |
public IEnumerable GetEvents(StrategyPattern.Enumerations.EventEnumerations.CustomerType customerType) | |
{ | |
//Hydrate list with mock data. Each event has a base price (used for the standard customer type) | |
IEnumerable events = new List() | |
{ | |
{new Event{ Name = "Symposium on Software Development", Date = new DateTime(2015, 12, 15), TicketPrice = new Price{ Cost = 150}, | |
Location = new Location{ Address1 = "55 test street", Address2="", Name="The Blue Theater", State="NY", Zipcode="11757" }}}, | |
{new Event{ Name = "SQL Injection and XSS Attacks", Date = new DateTime(2016, 1, 21), TicketPrice = new Price{ Cost = 75} , | |
Location = new Location{ Address1 = "55 test street", Address2="", Name="The Blue Theater", State="NY", Zipcode="11757" }}}, | |
{new Event{ Name = "Security in Software Development", Date = new DateTime(2015, 11, 14), TicketPrice = new Price{ Cost = 250}, | |
Location = new Location{ Address1 = "55 test street", Address2="", Name="The Blue Theater", State="NY", Zipcode="11757" }}} | |
}; | |
//Determine which strategy to apply based on the customer type | |
IDiscountStrategy discountStrategy = DiscountFactory.GetDiscountStrategyForCustomerType(customerType); | |
//Apply the discount selected to each product, and return the discounted collection | |
return events.ApplyDiscount(discountStrategy); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Determine which strategy to apply based on the customer type | |
IDiscountStrategy discountStrategy = DiscountFactory.GetDiscountStrategyForCustomerType(customerType); | |
//Apply the discount selected to each product, and return the discounted collection | |
return events.ApplyDiscount(discountStrategy); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Extensions | |
{ | |
public static class EventExtensions | |
{ | |
public static IEnumerable ApplyDiscount(this IEnumerable events, IDiscountStrategy discountStrategy) | |
{ | |
foreach (Event e in events) | |
e.TicketPrice.SetAndApplyDiscountStrategy(discountStrategy); | |
return events; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Enumerations | |
{ | |
public static class EventEnumerations | |
{ | |
public enum CustomerType | |
{ | |
[DiscountTypeAttribute(typeof(NullDiscountStrategy))] | |
Standard, | |
[DiscountTypeAttribute(typeof(StudentDiscountStrategy))] | |
Student, | |
[DiscountTypeAttribute(typeof(NotForProfitDiscountStrategy))] | |
NotForProfit, | |
[DiscountTypeAttribute(typeof(GroupDiscountStrategy))] | |
Group | |
} | |
} | |
} | |
//in another class you can define the attribute: | |
namespace StrategyPattern.Attributes | |
{ | |
public class DiscountTypeAttribute : Attribute | |
{ | |
public Type DiscountStrategy { get; set; } | |
public DiscountTypeAttribute(Type discountStrategy) | |
{ | |
DiscountStrategy = discountStrategy; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// | |
/// Retrieves a discount strategy associated to the customer type | |
/// | |
///Based on the type of customer, return the appropriate discount strategy /// the appropriate discount strategy | |
public static IDiscountStrategy GetDiscountStrategyForCustomerType(StrategyPattern.Enumerations.EventEnumerations.CustomerType customerType) | |
{ | |
//Retrieve the enumeration's attributes specified in Enumerations/EventEnumerations.cs | |
FieldInfo field = customerType.GetType().GetField(customerType.ToString()); | |
object[] attribs = field.GetCustomAttributes(typeof(DiscountTypeAttribute), false); | |
//If no attribute was specified, then return the normal price by default | |
if (attribs.Length == 0) | |
return new NullDiscountStrategy(); | |
return Activator.CreateInstance((attribs[0] as DiscountTypeAttribute).DiscountStrategy) as IDiscountStrategy; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Controllers | |
{ | |
public class EventController : ApiController | |
{ | |
readonly IEventRepository _eventRepository; | |
//NInject swaps IEventRepository with FakeEventRepository | |
public EventController(IEventRepository eventRepository) | |
{ | |
this._eventRepository = eventRepository; | |
} | |
// GET: api/Event?customerID=1 - pretend each customer, at the time of registration, only has one customer type based on who it is (a school, a non-profit, a student, or a general user). | |
public IEnumerable Get(int customerID) | |
{ | |
//Get the upcoming events, applying any discounts the customer might receive. There are several ways to do this, but I chose to pass the ID as a parameter. | |
return _eventRepository.GetEvents(UserService.GetCustomerTypeFromID(customerID)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace StrategyPattern.Services | |
{ | |
public static class UserService | |
{ | |
/// | |
/// Mock User Service to return a customer type based on the user id | |
/// | |
////// | |
public static StrategyPattern.Enumerations.EventEnumerations.CustomerType GetCustomerTypeFromID(int customerID) | |
{ | |
switch (customerID) | |
{ | |
case 1: | |
return Enumerations.EventEnumerations.CustomerType.Standard; | |
case 2: | |
return Enumerations.EventEnumerations.CustomerType.Student; | |
case 3: | |
return Enumerations.EventEnumerations.CustomerType.NotForProfit; | |
case 4: | |
return Enumerations.EventEnumerations.CustomerType.Group; | |
default: | |
return Enumerations.EventEnumerations.CustomerType.Standard; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment