First, what does this pattern solve? Well, it allows us to have a variable number of algorithms work against a particular object context.
Hmm... we kind of need to break this down a little.
I took a quick peek at the best website I know of for design patterns (Strategy Pattern) and they use the very excellent example of sorting algorithms.
Honestly, that might be enough of a tutorial for you but they use abstract classes and I have qualms with those so we're going to see how interfaces apply to this concept instead.
Also, instead of sorting algorithms (I hope you already reviewed the link), we'll do state tax on a product.
Let's get started:
Let's create a product interface for any products we may be encountering:
namespace StrategyPatternDemo
{
/// <summary>
/// Contract specification for all products in system.
/// </summary>
public interface IProduct
{
// The name of this product
string Name { get; }
// The base price of this product
decimal Price { get; }
}
}
For our product, let's keep things simple and just attribute a name and a price.
Let's pretend this is a chain of candy stores. We'll have some products that implement the IProduct interface:
namespace StrategyPatternDemo
{
public class JawBreaker : IProduct
{
public string Name { get; private set; }
public decimal Price { get; private set; }
public JawBreaker()
{
Name = "Jaw Breaker";
Price = .50M;
}
}
public class BubbleGum : IProduct
{
public string Name { get; private set; }
public decimal Price { get; private set; }
public BubbleGum()
{
Name = "Bubble Gum";
Price = .75M;
}
}
public class Taffy : IProduct
{
public string Name { get; private set; }
public decimal Price { get; private set; }
public Taffy()
{
Name = "Taffy";
Price = 1.25M;
}
}
}
You're going to notice that I'm actually going to get pretty detailed here with my example. And all just for a few lines of strategy?! Yeah...
See, design patterns are great and all but you really need to get the context behind them. If you don't know WHY you're using it, then you shouldn't be using it.
Anyway, let's create a chain of stores that sell our candy. Since I want different taxes and a quick google search gives me info I need (Sales Taxes) we have plenty of stores to make.
Of course, I'm only going to make 3 or so stores:
- Arizona = 6%
- Colorado = 4%
- California = 8%
Here's our code for an interface:
namespace StrategyPatternDemo
{
/// <summary>
/// Contract specification for all stores in system.
/// </summary>
public interface IStore
{
// The location of the store.
string Location { get; }
decimal SalesTax { get; }
// The total price of a product in a certain store.
decimal Total { get; }
}
}
What's that? you may be asking. Well, it's our strategy!
We'll have the interface require a total which is of course the calculated sales tax against the product's base price.
Let's make our stores based on the specification now:
namespace StrategyPatternDemo
{
// NOTE: Adding 1 to tax automatically adds it together for us.
public class ArizonaStore : IStore
{
public string Location { get; private set; }
public decimal Total { get; private set; }
// Arizona's tax is 6%
public decimal SalesTax
{
get { return 1.06M; }
}
public ArizonaStore(IProduct product)
{
Location = "Arizona";
Total = product.Price * SalesTax;
}
}
public class ColoradoStore : IStore
{
public string Location { get; private set; }
public decimal Total { get; private set; }
// Colorado's tax is 4%
public decimal SalesTax
{
get { return 1.04M; }
}
public ColoradoStore(IProduct product)
{
Location = "Colorado";
Total = product.Price * SalesTax;
}
}
public class CaliforniaStore : IStore
{
public string Location { get; private set; }
public decimal Total { get; private set; }
// California's tax is 8%
public decimal SalesTax
{
get { return 1.08M; }
}
public CaliforniaStore(IProduct product)
{
Location = "California";
Total = product.Price * SalesTax;
}
}
}
Before wrapping up, let's write a record entry:
namespace StrategyPatternDemo
{
public class RecordEntry
{
public string Location { get; set; }
public string SalesTax { get; set; }
public string CandyName { get; set; }
public string BasePrice { get; set; }
public string TotalPrice { get; set; }
public override string ToString()
{
return String.Format
(
"{0}, {1}, {2} : {3}, {4}",
Location,
SalesTax,
CandyName,
BasePrice,
TotalPrice
);
}
}
}
Well, we've written a lot of little bits here so let's write a Main and hook all this grit together. Before we do though, see what's happening? Based on the store we want, we will get a different total for the very same product. This is because with each store, the algorithm changes. However, calling code doesn't care!
namespace StrategyPatternDemo
{
class Program
{
static void Main(string[] args)
{
List<IProduct> CandyList = new List<IProduct>()
{
new JawBreaker(),
new BubbleGum(),
new Taffy()
};
List<RecordEntry> records = new List<RecordEntry>();
// First Arizona:
foreach (IProduct product in CandyList)
{
IStore store = new ArizonaStore(product);
records.Add(
new RecordEntry()
{
Location = store.Location,
SalesTax = String.Format("{0:P}", store.SalesTax - 1),
CandyName = product.Name,
BasePrice = String.Format("{0:C}", product.Price),
TotalPrice = String.Format("{0:C}", store.Total)
}
);
}
// Next Colorado:
foreach (IProduct product in CandyList)
{
IStore store = new ColoradoStore(product);
records.Add(
new RecordEntry()
{
Location = store.Location,
SalesTax = String.Format("{0:P}", store.SalesTax - 1),
CandyName = product.Name,
BasePrice = String.Format("{0:C}", product.Price),
TotalPrice = String.Format("{0:C}", store.Total)
}
);
}
// Finally California:
foreach (IProduct product in CandyList)
{
IStore store = new CaliforniaStore(product);
records.Add(
new RecordEntry()
{
Location = store.Location,
SalesTax = String.Format("{0:P}", store.SalesTax-1),
CandyName = product.Name,
BasePrice = String.Format("{0:C}", product.Price),
TotalPrice = String.Format("{0:C}", store.Total)
}
);
}
// Last but not least, print:
foreach (RecordEntry record in records)
{
Console.WriteLine(record.ToString());
}
// And a pause:
Console.ReadLine();
}
}
}
Quote
Arizona, 6.00 %, Jaw Breaker : $0.50, $0.53
Arizona, 6.00 %, Bubble Gum : $0.75, $0.80
Arizona, 6.00 %, Taffy : $1.25, $1.33
Colorado, 4.00 %, Jaw Breaker : $0.50, $0.52
Colorado, 4.00 %, Bubble Gum : $0.75, $0.78
Colorado, 4.00 %, Taffy : $1.25, $1.30
California, 8.00 %, Jaw Breaker : $0.50, $0.54
California, 8.00 %, Bubble Gum : $0.75, $0.81
California, 8.00 %, Taffy : $1.25, $1.35
Arizona, 6.00 %, Bubble Gum : $0.75, $0.80
Arizona, 6.00 %, Taffy : $1.25, $1.33
Colorado, 4.00 %, Jaw Breaker : $0.50, $0.52
Colorado, 4.00 %, Bubble Gum : $0.75, $0.78
Colorado, 4.00 %, Taffy : $1.25, $1.30
California, 8.00 %, Jaw Breaker : $0.50, $0.54
California, 8.00 %, Bubble Gum : $0.75, $0.81
California, 8.00 %, Taffy : $1.25, $1.35
And that's another way to work in the strategy pattern.
Here's that original pattern site again for your review: (Strategy Pattern).
Hope this helped. Have fun.





MultiQuote





|