Skip to content

Latest commit

 

History

History
116 lines (96 loc) · 4.56 KB

Strategy Pattern.md

File metadata and controls

116 lines (96 loc) · 4.56 KB

Strategy

Definition : Define a family of algorithms, encapsulate each one, and make them interchange­able. Strategy lets the algorithm vary independently from clients that use it.

Problems to Solve

  • How to avoid Hard-Wired Algorithms? Instead of code and algorithm directy in a class, create a new class what contains that code. This will help you to switch that algorithms.
  • How to Avoid conditional parameters? if your method have multiples algorithms inside for one purpose, you need a way to switch them, and instead of put conditional or true/false parameters in that, you can delegate an strategy for apply an specific algorithm.
  • how to reduce complicated classes with a lot of algorithms? Instead of have a class with a lot of algorithms for the same or related tasks, consider using one class for each one.

Applications

  • Change algorithms in run time : for example you are creating a Login for your application and there's multiple ways for that. Is not the same algorithm if you are taking an user and password instead of a facebook-login.
  • Refactoring tasks :
    • Converting hard-wired algorithms into reusable classes.
    • Replacing conditional statements with strategies.

Solution

The Strategy Pattern proposes the following :

  • You have a context what is the place where the strategies are called.
  • Then you need a Strategy Interface to implement in the specifict strategies.
  • And in third place you will have strategy 1, strategy2 and more... what implements the strategy interface.


Example

Suppose what you are building a kind of rustic paycheck generator and you want to be capable of export that in HTML or in markdown (for simplicity purposes of course).

So, you need a strategy for generate HTML and other for markdown. Firstly we'll code an interface for achieve that:

interface IDocStrategy
{
    StringBuilder sb { get; }
    IDocStrategy AddText(string text);
    IDocStrategy AddList(IEnumerable items);
    string GenerateDoc();
}

With this very simple interface we created two strategies :

class HtmlGenerator : IDocStrategy
{
    public StringBuilder sb { get; private set; } = new StringBuilder();
    public IDocStrategy AddText(string text)
    {
        sb.AppendLine($"<p>{text}</p>");
        return this; 
    }
    public IDocStrategy AddList(IEnumerable items)
    {
        sb.AppendLine("<ul>");
        foreach(var item in items)
            sb.AppendLine($"  <li>{item}</li>");
        sb.AppendLine("</ul>");
        return this;
    }
    public string GenerateDoc() => sb.ToString();
}
class MarkdownGenerator : IDocStrategy
{
    public StringBuilder sb { get; private set; } = new StringBuilder();
    public IDocStrategy AddText(string text)
    {
        sb.AppendLine(text);
        return this; 
    }
    public IDocStrategy AddList(IEnumerable items)
    {
        sb.AppendLine("");
        foreach(var item in items)
            sb.AppendLine($"- {item}");
        sb.AppendLine("");
        return this;
    }
    public string GenerateDoc() => sb.ToString();
}

Very simple implementations, which are used by the Paycheck class :

class PayCheck
{
    public static void GeneratePaycheck (IDocStrategy OutputFormat, string name, IEnumerable Payments)
    {
        string separator = "--------i-am-a-rustic-separator-bar--------";
        OutputFormat.AddText($"Name : {name}");
        OutputFormat.AddText(separator);
        OutputFormat.AddList(Payments);
        OutputFormat.AddText(separator);
        Console.WriteLine(OutputFormat.GenerateDoc());
    }
}

Note what we've used Dependency Injection for the IDocStrategy interface. I see this way a better approach instead of switching the strategies with a switch-case, because with that you are coupling the client with the strategies. But if you use dependency injection you can extend the output formats possible of the Paycheckclass.

In the main we've this code:

string person = "John Doe";
List<string> payments = new List<string>();
payments.Add("Salary : + 3.000");
payments.Add("Extra hours : + 500");
payments.Add("Income Tax : - 500");
payments.Add("Total : 3.000");
PayCheck.GeneratePaycheck(new MarkdownGenerator(), person, payments);
PayCheck.GeneratePaycheck(new HtmlGenerator(), person, payments);

There are hard-coded some chunks of data to test the different strategies and you can see the two outputs in the console, one in markdown and the other in HTML.