Strategy Pattern

Published by

on

Strategy Pattern

Strategy Pattern allows selection of an Algorithm at Runtime instead of design time. Instead of implementing a single algorithm directly , it allows one to choose from a family of algorithms on need basis. Strategy Pattern is part of Behavioural Design Patterns. More on types of Patterns here .

Lets understand this with an example using C# :

Problem : There are a wide variety of Ducks with each having its own way of walking or flying. Any kind of duck can be created and they should be able to walk or fly.

class StrategyPattern
{
    static void Main(string[] args)
    {
        Duck wildDuck = new WildDuck();
        Console.WriteLine(wildDuck.Walk());
    }
}

interface Duck
{
    string Walk();
}

class WildDuck : Duck
{
    public string Walk()
    {
        return "Walking-D";
    }
}

class CityDuck : Duck
{
    public string Walk()
    {
        return "Walking-E";
    }
}

class MountainDuck : Duck
{
    public string Walk()
    {
        return "Walking-F";
    }
}

class CloudDuck : Duck
{
    public string Walk()
    {
        return "Walking-F";
    }
}

There’s something fishy about the code that needs cleanup ? Let’s find out !

Observe carefully how CloudDuck and MountainDuck are similar in the way they Walk ?

The methods Walk( ) have duplicate piece of code in both CloudDuck and MountainDuck !! This could lead to bloated codebase and a bug in one method/function needs to be fixed in all the redundant methods across all the classes ( painful 😦 ).

Strategy Pattern was designed to solve the exact same problem.

Consider the following code sample where we introduce a behaviour interface and a corresponding behaviour class to move out the Duplicate code and make it a runtime call.

class StrategyPattern
{
    static void Main(string[] args)
    {
        Duck wildDuck = new WildDuck(new WalkBehaviourD());
        Console.WriteLine("wildDuck walks like " + wildDuck.Walk());

        Duck mountainDuck = new MountainDuck(new WalkBehaviourF());
        Console.WriteLine("mountainDuck walks like " + mountainDuck.Walk());

        Duck cloudDuck = new CloudDuck(new WalkBehaviourF());
        Console.WriteLine("cloudDuck walks like" + cloudDuck.Walk());
    }
}

class WalkBehaviourF : WalkBehaviour
{
    public string Walk()
    {
        return "Walking-F";
    }
}

class WalkBehaviourD : WalkBehaviour
{
    public string Walk()
    {
        return "Walking-D";
    }
}

interface Duck
{
    string Walk();
}

interface WalkBehaviour
{
    string Walk();
}

class WildDuck : Duck
{
    WalkBehaviour walkBehaviour;
    //this is also called constructor injection
    public WildDuck(WalkBehaviour b) 
    {
        walkBehaviour = b;
    }

    public string Walk()
    {
        return this.walkBehaviour.Walk();
    }
}

class MountainDuck : Duck
{
    WalkBehaviour walkBehaviour;
    public MountainDuck(WalkBehaviour b)
    {
        walkBehaviour = b;
    }

    public string Walk()
    {
        return this.walkBehaviour.Walk();
    }
}

class CloudDuck : Duck
{
    WalkBehaviour walkBehaviour;
    public CloudDuck(WalkBehaviour b)
    {
        walkBehaviour = b;
    }

    public string Walk()
    {
        return this.walkBehaviour.Walk();
    }
}

In the above sample code we can see that walkbehaviour now makes it easier to change the way a duck walks at runtime by passing the right behaviour. All the duplicate code now can be avoided. Based on the walkbehaviour that gets passed to the Duck Constructor( ) , the duck has a gait of choice :).

But all this effort to just add an extra layer of complication ? Actually , we could also get rid of all the Duck classes 🙂 . If observe carefully all we need is a single Duck class now that can be governed with just different behaviours !!

using System;

//WildDuck , CityDuck classes are no longer needed
class Duck
{
    WalkBehaviour walkBehaviour;
    FlyBehaviour flyBehaviour;

    //this is also called constructor injection
    public Duck(WalkBehaviour wb, FlyBehaviour fb)
    {
        walkBehaviour = wb;
        flyBehaviour = fb;
    }

    public string Fly()
    {
        return this.flyBehaviour.Fly();
    }

    public string Walk()
    {
        return this.walkBehaviour.Walk();
    }
}

/// 
/// Walking Algorithms for Ducks.
/// 

interface WalkBehaviour
{
    string Walk();
}

class WalkBehaviourF : WalkBehaviour
{
    public string Walk()
    {
        return "Walking-F";
    }
}

class WalkBehaviourD : WalkBehaviour
{
    public string Walk()
    {
        return "Walking-D";
    }
}

/// 
/// Flight Algorithms for Ducks.
/// 
interface FlyBehaviour
{
    string Fly();
}

class FlyBehaviourA : FlyBehaviour
{
    public string Fly()
    {
        return "Flying-A";
    }
}

class FlyBehaviourB : FlyBehaviour
{
    public string Fly()
    {
        return "Flying-B";
    }
}

class StrategyPattern
{
    static void Main(string[] args)
    {
        //Ducks with different behaviours can be created at runtime 
        //and make use of different ways to walk and fly !
        Duck wildDuck = new Duck(new WalkBehaviourD(), new FlyBehaviourA());
        Console.WriteLine("wildDuck walks like " + wildDuck.Walk());

        Duck mountainDuck = new Duck(new WalkBehaviourD(), new FlyBehaviourB());
        Console.WriteLine("mountainDuck flies like " + mountainDuck.Fly());

        Duck cloudDuck = new Duck(new WalkBehaviourF(), new FlyBehaviourB());
        Console.WriteLine("cloudDuck walks like " + cloudDuck.Walk() + " flies like " + cloudDuck.Fly());
    }
}

// Output :
//wildDuck walks like Walking-D
//mountainDuck flies like Flying-B
//cloudDuck walks like Walking-F flies like Flying-B

In the above code snippet we are creating a wild Duck with ease using the combination of different duck behaviours ( Also called Dependency Injection ). Note that we now also have a Fly behaviour for a Duck. We have removed all the different variety of Duck classes like WildDuck , CityDuck et and made it much simpler.

Code snippet on Github 

We can concoct any Duck of our choice having distinct behavioural traits associated with them. With Strategy Pattern we can accomplish the following:

  • Remove all the duplicate code
  • Remove extra classes used for defining a variety of an Object
  • Allow Dependency injection
  • Bugs are localised to a behaviour as code is not replicated across each of the classes.