How to implement the Strategy Design Pattern?

In software applications, CHANGE is something you need to accept and be prepared for. Your coding must not be concrete, but flexible. This is the idea of Design patterns. It helps make your game flexible, modular, maintainable and adaptable.

The Strategy Design Pattern helps you game or application be flexible by allowing you to change a behavior dynamically without the need of modifying the class.

Let's review some design principles that make up the Strategy Design Pattern.

Design Principle 1

Always encapsulate what varies

Always, always, always look at your class and look what behaviors will vary with different instances of your class. If you find any, encapsulate them. For example, let's say that you have a Duck class. In this class you have two set of behaviors: Quack and Fly.

Listing 1
class Duck{

void quack();
void fly();
};

Imagine further that certain types of ducks are inheriting the Duck class:

Listing 2
class MallardWoodenDuck:public Duck{
//...code goes here
};

class RubberDuck:public Duck{
//...code goes here
};

Now, it all looks good except that a Mallard Wooden duck and a Rubber duck do not quack nor fly. These behaviors will vary among different set of ducks.

Thus, it is better to encapsulate these behaviors outside the Duck class. Instead of having them as inherit behaviors, we will use them as composite behaviors.

The Duck behaviors will live in separate classes. The beauty of it is that the Duck class do not need to know any implementation of its own behaviors.

Let's take a look at our second design principle.

Design Principle

Code to the Interface, not to the implementation

What this means is to always code to the superclass instead of the concrete class. For example:

Assume that Beagle and Pittbull are subclasses of a Dog class. Instead of coding to the implementation as shown in listing 3:

Listing 3
Beagle *myBeagle=new Beagle(); //instance of beagle
Pittbull *myPittbull=new Pittbull(); //instance of pittbull

Always code to the interface, i.e. to the super-class, as shown in listing 4.

Listing 4
Dog *myBeagle=new Beagle(); //instance of beagle
Dog *myPittbull=new Pittbull(); //instance of pittbull

By coding to the interface, you provide more flexibility to your game or app. Your game will be able to be extended without the need to go into previous written code and modify it. Which brings me to the third design principle:

Design Principle

Open for extension, close for modification.

Your game code should always be CLOSED for modification but OPEN for extension. This means that 100 years from now, a coder should be able to add functionality to your game without the need to modify your original code.

How do you provide this flexibility? Say hello to the Strategy Design Pattern.

Implementing the Strategy Design Pattern

Let's take all the three principles mentioned above and implement the Strategy Design Pattern. We are going to turn the class shown in listing 5 and turn it into a very flexible, powerful class.

Listing 5
class Duck{
  void quack();
  void fly();
};

Always encapsulate what varies

We know that not every duck quacks nor fly. Thus, these behaviors should be encapsulated. We are going to move these behaviors into their own classes. Better yet, we are going to make them Interfaces.

What is an Interface?

Think of an interface as a class which only defines its methods (behavior) but does not have any implementation code for them. Moreover, an interface does not contain a constructor. This implies that you can never create an instance of an Interface. You can only have classes that inherit the behaviors of the interface.

You may also think of an “Interface” as a blueprint. A blueprint which contains a list of all the behaviors which your subclasses must implement and abide to.

Creating an interface?

How do you create an interface in C++? C++ do not have interfaces. Instead it has the concept of Virtual Classes.

A virtual class is created by declaring all methods of the class virtual and equating them to zero as shown in listing 6.

Listing 6
class MyVirtualClass{
  public:
      virtual void talk()=0;
};

Encapsulating the Duck behaviors

We are going to encapsulate the quack() and fly() behaviors. We will do that with virtual classes in C++ as shown in listing 7.

Listing 7
class QuackBehavior{

  public:
      virtual void quack()=0;
  };

class FlyBehavior{

  public:

      virtual void fly()=0;

};

Next, we are going to create subclasses that will implement the actual quack and fly behaviors. Let's start by making two subclasses that will implement the fly behavior. Both of these classes will be subclasses of the the interface FlyBehavior. We will name these subclasses:

  • ICanFly
  • IDontFly
Listing 8 ICanFly.h
class ICanFly:public FlyBehavior{ //notice how ICanFly is a subclass of the interface FlyBehavior

  private:

  public:

      ICanFly(){};
      ~ICanFly(){};

      void fly(); //fly() is one of the method which needs to be implemented by ICanFly since ICanFly has agreed to implement all of FlyBehavior methods.
};

Listing 9 shows the header file for the ICanFly class. Note that ICanFly is required to implement the fly() method. This is shown in listing 9.

Listing 9 ICanFly.cpp
#include "ICanFly.h"

void ICanFly::fly(){

    //...Output the duck can fly
    cout<<"I can fly"<<endl;
}

Now, lets implement the IDontFly class. This will be similar to the ICanFly class, except that the fly() method will be different.

Listing 10 IDontFly.h
class IDontFly:public FlyBehavior{

private:

public:
    IDontFly(){};
    ~IDontFly(){};

    void fly();
};

As opposed to the IcanFly class, the fly() method will show that the duck can't fly.

Listing 11 IDontFly.cpp
void IDontFly::fly(){

     //...Output the duck can't fly
    cout<<"I don't fly"<<endl;
}

The same idea is implemented in the subclasses of the QuackBehaviors class. One class with perform a quack, the other will not.

Adding flexibility to the Duck class

Now, we are going to remove the fly() and quack() methods shown in listing 5 and composed the Duck class with the new class behaviors as shown in listing 12.

Listing 12 Modified Duck class
class DuckClass{

protected:
    //1. Both behavior variables are declared as the behavior INTERFACE type
    FlyBehavior *flyBehavior; 
    QuackBehavior *quackBehavior;

public:

    DuckClass(){};
    ~DuckClass(){};

    //2. Prints out if the duck can quack or not depending on the behavior set
    void performQuack(); 

    //3. Prints out if the duck can fly or not depending on the behavior set
    void performFly();  

    //4. sets a fly behavior
    void setFlyBehavior(FlyBehavior *uFlyBehavior); 

    //5. sets a quack behavior
    void setQuackBehavior(QuackBehavior *uQuackBehavior); 

};

In line 1, you can see that the behaviors will be set dynamically through the interface classes.

The methods performFly() and performQuack() will delegate the behaviors to the interfaces as shown in listing 13.

Listing 13
void DuckClass::performQuack(){

    quackBehavior->quack(); //QuackBehavior quack() method gets called. It prints if the duck can quack or not.

}

void DuckClass::performFly(){

    flyBehavior->fly(); //FlyBehavior fly() method gets called. It prints if the duck can fly or not.

}

We can change the behavior of our class dynamically by simply calling the methods shown in lines 4 & 5. These methods are shown in listing 14.

Listing 14
void DuckClass::setFlyBehavior(FlyBehavior *uFlyBehavior){

    flyBehavior=uFlyBehavior; //set the fly behavior
}

void DuckClass::setQuackBehavior(QuackBehavior *uQuackBehavior){

    quackBehavior=uQuackBehavior; //sets the quack behavior
}

So now, I can create any Duck subclass, and I can change its behavior at any time by setting a different behavior interface.

Let's see this in action.

We are going to create a subclass of Duck called MallardDuckClass.

Listing 15
class MallardDuckClass:public DuckClass{

private:

public:

    MallardDuckClass(){
        //1. Create instances of the desired behaviors
        quackBehavior=new ICanQuack();
        flyBehavior=new ICanFly();

    };

    ~MallardDuckClass(){};

};

In the constructor method, I'm creating a new instance of ICanQuack class and setting it the quackBehavior. The Same goes for the flyBehavior (line 1).

This means that if I call performFly() or performQuack(), the ICanFly() and ICanQuack() methods (i.e. quack() and fly()) will be executed.

Now in our main.cpp file, we are going to create an instance of the MallardDuckClass and change its behaviors dynamically.

Listing 16
int main(int argc, const char * argv[])
{
    //1. I create a mallard duck class
    //Notice how I code to the interface, not the implementation
    DuckClass *mallard=new MallardDuckClass();  

    //2. performQuack prints that the mallard duck does quack
    mallard->performQuack(); 

    //3. performFly prints that the mallard duck does fly
    mallard->performFly(); 

    //4. Now I'm going to create a new behaviors which takes away the quacking and flying from the mallard duck

    QuackBehavior *nowICantQuack=new IDontQuack();
    FlyBehavior *nowICantFly=new IDontFly();

    //5. I'm going to set the new quack behaviors to the mallard duck
    mallard->setFlyBehavior(nowICantFly); 

    //6. I'm going to set the new fly behaviors to the mallard duck
    mallard->setQuackBehavior(nowICantQuack);

    //7. performQuack prints that the mallard duck does NOT quack
    mallard->performQuack(); 

    //8. performFly prints that the mallard duck does NOT fly
    mallard->performFly(); 

}

Notice something important in line 1. When creating the instance of the mallard class, I coded to the superclass, not the implementation.

Since the mallard class was assigned behaviors where it can quack and fly, lines 2 & 3 will show that the duck can fly and quack.

In line 4, I instantiate new behaviors for the mallard class and assign them in lines 5 & 6.

Now, when the mallard is asked to quack and fly, it outputs new behaviors (lines 7 & 8).

This is the beauty of the Strategy Design Pattern. You can change behaviors dynamically. Furthermore, if you need new behaviors, you do not need to modify your Duck class nor your interface classes. You simply create a subclass and implement the new quack and fly behaviors.

Your class is now Closed for modification, Open for extension.

Source code

I hope this post has been helpful. You can find the source code for the Strategy Design Pattern here.

Questions?

So, do you have any questions? Is there something you need me to clarify? Did this post help you? Please let me know. Add a comment below and subscribe to receive our latest game development projects.

Harold Serrano

Computer Graphics Enthusiast. Currently developing a 3D Game Engine.