One of the design patterns I have used often in my career is the Strategy Pattern. This is a very powerful pattern. I like the definition from my favorite patterns book, “Head First Design Patterns: A Brain-Friendly Guide“
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
I wrote up an example using C++ that I’d like to share to help show what the Strategy Pattern is all about. I have decided to take three of our favorite Star Wars characters, Han Solo, Luke Skywalker and Chewy. Each one of them has their own weapon of choice. Han likes to use his Blaster, Luke, of course, uses a light saber and Chewy uses a crossbow. Can you see what might be the family of algorithms (or set of behaviors) from this description? If you chose the use of weapons, the force is strong with you! Well done!
So let’s create our interface to the algorithms and call it WeaponBehavior which has an abstract method useWeapon().
class WeaponBehavior
{
public:
virtual ~WeaponBehavior() {}
virtual void useWeapon() = 0;
};
The three weapon behaviors would implement this interface. Let’s name them BlasterBehavior, LightSaberBehavior and CrossBowBehavior.
class BlasterBehavior: public WeaponBehavior
{
public:
BlasterBehavior();
virtual ~BlasterBehavior();
virtual void useWeapon()
{
std::cout << "I am using my blaster!\n";
}
};
class LightSaberBehavior: public WeaponBehavior
{
public:
LightSaberBehavior();
virtual ~LightSaberBehavior();
virtual void useWeapon()
{
std::cout << "I am using my light saber!\n";
}
};
class CrossBowBehavior: public WeaponBehavior
{
public:
CrossBowBehavior();
virtual ~CrossBowBehavior();
virtual void useWeapon()
{
std::cout << "I am using my cross bow!\n";
}
};
So what clients would make use of our WeaponBehaviors? Our Star Wars characters, of course! I created a class called StarWarsCharacter that implements a Fight() method that delegates the behavior to its WeaponBehavior member that is initialized by a parameter passed into the constructor. It also has a string member for the name of the character that is also initialized with a parameter passed into the constructor. We have a SetWeaponBehavior() method to allow changing our character’s WeaponBehavior dynamically. Doing so demonstrates the flexibility of the Strategy Pattern and its interchangeability of its behavior without changing the StartWarsCharacter implementation. The relationship between the StarWarsCharacter class and the WeaponBehavior interface is one of composition. In other words, the StarWarsCharacter is being composed with the right WeaponBehavior object. This is a good example of using the design principle to favor composition over inheritance.
#include
class WeaponBehavior;
class StarWarsCharacter
{
public:
StarWarsCharacter(std::string name,
WeaponBehavior * weaponBehavior);
virtual ~StarWarsCharacter();
virtual void Fight();
virtual void SetWeaponBehavior(WeaponBehavior * weaponBehavior)
{ m_pWeaponBehavior = weaponBehavior; }
protected:
WeaponBehavior * m_pWeaponBehavior;
std::string m_name;
};
Ok, so now we have our classes defined. Let’s write a program to put them to use. Below we start with creating our three different WeaponBehaviors. Next create our three StarWarsCharacters and pass the preferred WeaponBehavior in the constructors as well as the name of the character. Now we can demonstrate the the use of these WeaponBehaviors for each character by calling the Fight() method (which just prints the behavior being used). After that, lets demonstrate the change of the behavior at runtime by changing the WeaponBehavior for each character and then calling Fight() again to show they now have different WeaponBehaviors.
#include
#include "BlasterBehavior.h"
#include "CrossBowBehavior.h"
#include "LightSaberBehavior.h"
#include "StarWarsCharacter.h"
int main()
{
WeaponBehavior * blasterBehavior = new BlasterBehavior();
WeaponBehavior * lightSaberBehavior = new LightSaberBehavior();
WeaponBehavior * crossBowBehavior = new CrossBowBehavior();
StarWarsCharacter * hanSolo =
new StarWarsCharacter(std::string("Han Solo"),
blasterBehavior);
StarWarsCharacter * lukeSkywalker =
new StarWarsCharacter(std::string("Luke Skywalker"),
lightSaberBehavior);
StarWarsCharacter * chewy =
new StarWarsCharacter(std::string("Chewy"),
crossBowBehavior);
hanSolo->Fight();
lukeSkywalker->Fight();
chewy->Fight();
hanSolo->SetWeaponBehavior(lightSaberBehavior);
lukeSkywalker->SetWeaponBehavior(crossBowBehavior);
chewy->SetWeaponBehavior(blasterBehavior);
hanSolo->Fight();
lukeSkywalker->Fight();
chewy->Fight();
delete hanSolo;
delete lukeSkywalker;
delete chewy;
delete blasterBehavior;
delete lightSaberBehavior;
delete crossBowBehavior;
return 0;
}
Here is the output.
Han Solo: I am using my blaster!
Luke Skywalker: I am using my light saber!
Chewy: I am using my cross bow!
Han Solo: I am using my light saber!
Luke Skywalker: I am using my cross bow!
Chewy: I am using my blaster!
Feel free to download the code here
Well there you have it. I hope you see how powerful this pattern is and how it can be applied. I hope you enjoyed this post. I plan to do more Design Pattern posts in the future, so if you have any requests please let me know or feel free to subscribe.
Recommended Resources
Here are some resources I recommend for more information on the Strategy Pattern
Head First Design Patterns: A Brain-Friendly Guide
Design Patterns: Elements of Reusable Object-Oriented Software