C# Design Pattern : Strategy

February 10, 2011 by C#   Design Patterns   Architecture  

The strategy design pattern defines a means by which we decouple a family of related algorithms and make them interchangeable among one another within a required context.


The interchangeable bits are referred to as strategies, generally one would define a strategy by way of interface or abstract class e.g.

interface IStrategy
{
    bool DoSomething();
}

/*

abstract class Strategy
{
    abstract public bool DoSomething(); 
}

*/

class StrategyA : IStrategy
{
    public bool DoSomething()
    {
        return true;
    }
}

class StrategyB : IStrategy
{
    public bool DoSomething()
    {
        return true;
    }
}

In the following snippet (console app) a strategy gets assigned according to what the user passed to the command-line - which represents our context.

class Context
{
    private IStrategy _Strategy;

    public Context(IStrategy Strategy)
    {
        this._Strategy = Strategy;
    }

    public bool DoSomething()
    {
        return this._Strategy.DoSomething();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Context ctx = ((args.Length > 0) && (args[0] == "a"))
            ? new Context(new StrategyA())
            : new Context(new StrategyB());
        ctx.DoSomething();
    }
}

It is rather obvious to imagine the usefulness of this pattern in the real world.

Imagine for a moment that we need to copy a file to a server somewhere, there is a few things (among others) to consider:
  • Location of the server in relation to the file we want to copy, outside/inside our network?
  • Available transfer methods ftp/http(soap)/ssh etc

If we implement the strategy pattern, we're basically going to decouple & handle these various scenarios using separate strategy classes - according to the context the program finds itself in (e.g. the app is outside the LAN - use FTP strategy), we'll know which strategy to use.

The beauty of this approach is that since its decoupled, the app doesn't need to have knowledge about the implementations of our strategies. When creating a strategy we simply need to implement the Strategy interface / abstract class and ensure that the behaviour from our implementations are predictable.

The following snippet is an example of this ftp/local scenario, we've got a FTPStrategy and a LocalStrategy (its behaviour might not be completely predictable e.g. error handling wise):

interface IStrategy
{
    public void UploadFile(String fullname, String folder);
}

class FTPStrategy : IStrategy
{
    public void UploadFile(string fullname, string folder)
    {
        FileInfo fileInfo = new FileInfo(fullname);

        Uri requestUri = new Uri(String.Concat("ftp://example.com", folder, "/", fileInfo.Name));
        FtpWebRequest ftpRequest = (FtpWebRequest)FtpWebRequest.Create(requestUri);
        ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;
        ftpRequest.Credentials = new NetworkCredential("username", "password");

        using (Stream writer = ftpRequest.GetRequestStream())
        {
            byte[] contents = File.ReadAllBytes(fullname);
            writer.Write(contents, 0, contents.Length);
        }
    }
}

class LocalStrategy : IStrategy
{
    public void UploadFile(string fullname, string folder)
    {
        FileInfo fileInfo = new FileInfo(fullname);
        byte[] contents = File.ReadAllBytes(fullname);
        File.WriteAllBytes(String.Concat("c:", folder, "/", fileInfo.Name), contents);
    }
}

In the following snippet the dev needs to tell the app via enum which strategy to use, it would probably make sense to rather detect which strategy is the most appropriate to use in context - instead of requiring the dev to specify strategy via enum, but hey ;)

class Context
{
    private IStrategy _Strategy;

    public Context(Strategies strategy)
    {
        if (strategy == Strategies.FTP)
        {
            _Strategy = new FTPStrategy();
        }
        else if (strategy == Strategies.Local)
        {
            _Strategy = new LocalStrategy();
        }
    }

    public void UploadFile(string fullname, string folder)
    {
        this._Strategy.UploadFile(fullname, folder);
    }
}

enum Strategies
{
    FTP,
    Local
}

class Program
{
    static void Main(string[] args)
    {
        Context ctx = new Context(Strategies.FTP);
        ctx.UploadFile(@"c:\somefolder\xls.xml", "/Skool");
    }
}

Incidentally I recently used this pattern in one of my PHP projects, have a look over here.

Additional Reading
http://www.dofactory.com/Patterns/PatternStrategy.aspx
http://www.netobjectives.com/PatternRepository/index.php?title=TheStrategyPattern


Leave a Comment