Skip to content

Instantly share code, notes, and snippets.

@karbyninc
Last active July 4, 2018 10:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karbyninc/48fff4ed97491bc2b9d4 to your computer and use it in GitHub Desktop.
Save karbyninc/48fff4ed97491bc2b9d4 to your computer and use it in GitHub Desktop.
The Strategy Pattern
namespace StrategyPattern.Contracts
{
public interface IDiscountStrategy
{
decimal ApplyDiscount(decimal price);
}
}
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;
}
}
}
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);
}
}
}
//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);
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;
}
}
}
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;
}
}
}
///
/// 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;
}
}
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));
}
}
}
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