Design Patterns: Strategy Pattern

By | 2015-11-14

Design Patterns are simply that, patterns used to solve common problems in designing software and provide us a with the answers to hard lessons learned by developers before us. Not only this, but design patterns also enable us to communicate succinctly with other developers on the strategy used to accomplish tasks. It is much easier to tell another developer that you used ‘such-n-such’ pattern than to re-hash all the intricate details of what you coded. In this series of posts we will take an introductory look at several different patterns commonly used in the software world.

Strategy Pattern
The Strategy Design Pattern is a design pattern where the behavior of an object can be changed at runtime. This pattern makes objects much more dynamic and lightweight, with most of the implementation abstracted out into a base class and interfaces. It is the fact that much of the implementation is abstracted out into separate encapsulated pieces that makes it easy to change behavior at runtime. To illustrate this point we are going to look at a small example. We are going to look at dogs. Dogs come in all sizes, shapes, and behaviors. To keep this simple we will focus on two features that change between different types of dogs….their bark and their personality. Some are lazy, some are hyper, some have a high-pitched bark, some have a deep bark. With this information in mind we will create separate interfaces to make it easy down the road to change implementations for both the types of barks and the types of personalities.

Interfaces

public interface IBarkBehavior
{
    void Bark();
}

and

public interface IPersonality
{
    void Personality();
}

Interfaces by themselves don’t do us much good without some implementations that use these interfaces. For the Bark behavior we will make two implementations, one for dogs that go “Ruff! Ruff!” and one for dogs that go “Yip!Yip!”.

Implementations

public class Ruff : IBarkBehavior
{
    public void Bark()
    {
        Console.WriteLine("Ruff! Ruff!");
    }
}

and

public class YipYip : IBarkBehavior
{
    public void Bark()
    {
        Console.WriteLine("YipYip!");
    }
}

And a couple implementations for our personality types:

public class Hyper : IPersonality
{
    public void Personality()
    {
        Console.WriteLine("I'm hyper!");
    }
}

and

public class Lazy : IPersonality
{
    public void Personality()
    {
        Console.WriteLine("I'm lazy.");
    }
}

Now these two interfaces and the subsequent implementations are just characteristics of our dogs. We now need a base Dog class that can be implemented in different dog implementations. Let’s create an abstract base class that has the following:

  • private fields to store bark and personality interface types
  • an abstract method our various dog types will override to display the type of dog
  • public methods to display the personality type and perform a bark
  • public methods that can change the values for our IBarkBehavior and IPersonality fields at runtime

Base Class

public abstract class Dog
{
    IBarkBehavior barkBehavior;
    IPersonality personality;

    public abstract void TypeOfDog();

    public void PerformBark()
    {
        barkBehavior.Bark();
    }

    public void GetPersonality()
    {
        personality.Personality();
    }

    public void SetBarkBehavior(IBarkBehavior behavior)
    {
        barkBehavior = behavior;
    }

    public void SetPersonalityType(IPersonality personalityType)
    {
        personality = personalityType;
    }
}

Now all we need are various implementations of our abstract Dog base class. In the constructors of our implementations we will call the SetBarkBehavior and SetPersonalityType methods so that our dogs have default implementations. However, because of the construction of our base class we can change those implementations at anytime. These classes will be fairly lightweight because much of the implementation details are in the abstract base class or the IBarkBehavior and IPersonality interface implementations.

We will create a Bulldog, who is lazy and has a bark of “Ruff!Ruff!”:

public class Bulldog : Dog
{
    public Bulldog()
    {
        SetBarkBehavior(new Ruff());
        SetPersonalityType(new Lazy());
    }

    public override void TypeOfDog()
    {
        Console.WriteLine("I'm a Bulldog");
    }
}

Then we will create a Chihuahua that “Yips!” and is hyper:

public class Chihuahua : Dog
{
    public Chihuahua()
    {
        SetBarkBehavior(new YipYip());
        SetPersonalityType(new Hyper());
    }
    public override void TypeOfDog()
    {
        Console.WriteLine("I'm a Chihuahua");
    }
}

Now we can instantiate one of these types of dogs and call our various methods:

Bulldog bulldog = new Bulldog();
bulldog.TypeOfDog();
bulldog.PerformBark();
bulldog.GetPersonality();

Output:
I'm a Bulldog
Ruff! Ruff!
I'm lazy.

However, because of the strategy pattern implementation we can easily change the implementation details at runtime and make our Bulldog “Yip!”:

bulldog.SetBarkBehavior(new YipYip());
bulldog.PerformBark();

Output:
Yip!Yip!

The flow of logic goes from our Bulldog and Chihuahua implementations up to the base class of Dog, which points to the IBarkBehavior and IPersonality interfaces to get implementations of Bark and Personality. At a high level it looks like this:
Strategy Design Pattern Diagram