Design Patterns: Strategy Pattern

Strategery...

Suppose you have a collection of things. Let's use dogs as an example. There are big dogs, little dogs, hairy dogs, hairless dogs, etc. All dogs do a few few different thing though: bark, sleep, and eat. If you were writing this in code, it'd be tempting to have a superclass called Dog from which other types (GoldenRetriever and Pomeranian) could inherit.

Code Snippet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Dog
{
  public void Bark()
  {
	//nothing here...will overwrite in each subclass
  }

  public void Sleep()
  {
	//code to make dog sleep
  }

  public void Eat()
  {
	//code to make dog eat
  }
}

public class GoldenRetriever : Dog
{
  public void Bark()
  {
	//Code to make a deep barking sound
  }
}

public class Pomeranian : Dog
{
  public void Bark()
  {
	//Code to make a yappy barking sound
  }
}

This is all well and good until it's decided that there should be another type of dog added: StuffedAnimalDog. StuffedAnimalDog doesn't bark or eat. He only sleeps. One option would be to implement it in the following way:

Code Snippet
1
2
3
4
5
6
7
8
9
10
11
12
public class StuffedAnimalDog : Dog
{
  public void Bark()
  {
	//do nothing
  }

  public void Eat()
  {
	//do nothing
  }
}

It's a bit of a pain to implement unnecessary methods though, and if more functionality it added to the Dog superclass (Running, jumping, swimming, etc.) these additional methods will likewise need to be implemented on the StuffedAnimalDog class for no additional functionality. We don't want StuffedAnimalDog swimming across our computer screen!

What would really be nice to do is isolate that which varies from that which does not. Then, when something changes, we only have to update code in one place. Thus, our code becomes more maintainable and extensible.

Enter...the Strategy Pattern.

The Strategy Pattern isolates behaviours from those objects which behave them. In this case, that means isolating the types of dogs from the behaviours (barking, eating, sleeping) that they do. Sometimes a line of code is worth a thousand words, so I'll just jump straight to what this looks like.

Code Snippet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Dog
{
  public IBarkBehavior BarkBehavior { get; set; }
  public IEatBehavior EatBehaviour { get; set; }

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

  public void Sleep()
  {
	//code to make dog sleep
  }

  public void PerformEat()
  {
	EatBehaviour.Eat();
  }
}

public interface IEatBehavior
{
  void Eat();
}

public interface IBarkBehavior
{
  void Bark();
}

public class DeepBark : IBarkBehavior
{
  public void Bark()
  {
	//Code to make a deep barking sound
  }
}

public class YappyBark : IBarkBehavior
{
  public void Bark()
  {
	//Code to make a yappy barking sound
  }
}

public class FastEat : IEatBehavior
{
  public void Eat()
  {
	//Code to eat really food really fast
  }
}
	
public  class PickyEater : IEatBehavior
{
  public void Eat()
  {
	//Code to eat only cubed filet mignon
  }
}
The final piece is the individual classes of dog.
Code Snippet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GoldenRetriever : Dog
{
  public GoldenRetriever()
  {
	BarkBehavior =  new DeepBark();
	EatBehaviour = new FastEat();
  }
}

public class Pomeranian : Dog
{
  public Pomeranian()
  {
	BarkBehavior = new YappyBark();
	EatBehaviour = new PickyEater();
  }
}

The algorithms (fasting eating, yappy barking, etc) can now vary independently of the clients that use them. You can change the exact behavior of the DeepBark class without having to make any changes to the GoldenRetriever class, much less Pomeranian or StuffedAnimalDog classes. As additional types of behavior (SwimBehavior, etc) are needed, the interface can be created (ISwimBehavior)and the property added to the Dog class. As types of SwimBehavior are needed (NoSwim,FastSwim, etc.), they can be created to implement ISwimBehavior and set the specific actions in their respective classes. Then each type of dog can simply set the SwimBehavior they implement.

Moreover, the behavior can actually be set at run time. Suppose we wanted the GoldenRetriever class to vary its bark depending on if it is playing or defending. When playing, it may implement the YappyBark() but when defending it may implement the DeepBark(). This can be accomplished by adding a method to the Dog class as follows.

Code Snippet
1
2
3
4
public void SetBarkBehavior(IBarkBehavior barkBehavior)
{
  BarkBehavior = barkBehavior;
}
Now you can do the following at run time:
Code Snippet
1
2
3
4
5
6
7
8
9
public void SampleProgram()
{
  var goldenRetriever = new GoldenRetriever();
  goldenRetriever.BarkBehavior.Bark();
  //output will be a deep bark
  goldenRetriever.SetBarkBehavior(new YappyBark());
  goldenRetriever.BarkBehavior.Bark();
  //output will be a yappy bark
}

portrait title

About Scott

I am a software engineer from Bozeman, MT enjoying the slightly warmer climate of Colorado. I think code can change lives. I think lives are worth changing. I write code.

You can find me on Twitter, LinkedIn, , and Github.