Skip to content

Strategy Design Pattern — Study Notes

1. The Problem: Inheritance Explosion

Suppose we have a Robot with different behaviors:

  • Talkable → talk()
  • Walkable → walk()
  • Flyable → fly()

Initially, inheritance looks convenient:

Robot
 ├── Talkable
 │    ├── NormalTalk
 │    └── NonTalk
 │
 ├── Walkable
 │    ├── NormalWalk
 │    └── NonWalk
 │
 └── Flyable
      ├── NormalFly
      └── NonFly

But the problem appears when we create actual robot types.

For example:

Normal Robot      → talks + walks + doesn't fly
Companion Robot   → talks + walks + flies
Worker Robot      → doesn't talk + walks + doesn't fly
...

The problem

If behavior is handled using inheritance, every combination of behaviors may require a separate subclass.

As the number of behaviors and variations increases, the class hierarchy becomes very large and difficult to maintain.

Example

If we have:

  • 2 types of talking
  • 2 types of walking
  • 2 types of flying

Then potentially:

2 × 2 × 2 = 8 combinations

With more behaviors, combinations grow rapidly.

This is commonly called inheritance explosion / class explosion.


2. The Core Problem

The real issue is:

Behavior is tightly coupled to the class hierarchy.

If a behavior changes, we may need to:

  • create a new subclass,
  • modify the hierarchy,
  • or duplicate behavior across classes.

For example:

class NormalRobot : public Robot { ... };
class FlyingRobot : public Robot { ... };
class TalkingFlyingRobot : public Robot { ... };
class NonTalkingFlyingRobot : public Robot { ... };

This does not scale well.


3. The Key Idea Behind Strategy Pattern

Instead of inheriting behavior:

Take the behavior out of the main class and represent it as a separate object.

Then the main class has behaviors rather than is a behavior.

Before

Robot
   ↓
Inheritance
   ↓
Different Robot subclasses

After

             ┌──────────────┐
             │    Robot     │
             └──────┬───────┘
                    │
       ┌────────────┼────────────┐
       ↓            ↓            ↓
   Talkable      Walkable      Flyable
       │            │            │
       ↓            ↓            ↓
   Strategy      Strategy      Strategy

The Robot delegates the actual behavior to these strategy objects.


4. Definition

Strategy Pattern defines a family of algorithms/behaviors, encapsulates each one separately, and makes them interchangeable at runtime.

The important parts are:

1. Family of behaviors

Example:

Talk
├── NormalTalk
└── NonTalk
Walk
├── NormalWalk
└── NonWalk
Fly
├── NormalFly
└── NonFly

2. Encapsulate separately

Each behavior gets its own class.

class Talkable {
public:
    virtual void talk() = 0;
};
class NormalTalk : public Talkable {
public:
    void talk() override {
        // normal talking
    }
};
class NonTalk : public Talkable {
public:
    void talk() override {
        // cannot talk
    }
};

5. Composition Instead of Inheritance

This is the major design shift.

Inheritance approach

Robot
  ↓
Robot subclass
  ↓
More subclasses
  ↓
More combinations

Strategy approach

Robot
 ├── has a Talkable
 ├── has a Walkable
 └── has a Flyable

So:

Inheritance → "is-a" relationship Composition → "has-a" relationship

Here:

class Robot {
    Talkable* talkBehavior;
    Walkable* walkBehavior;
    Flyable* flyBehavior;
};

The robot has behaviors.


6. Strategy Structure

There are usually 3 important parts.

1. Strategy Interface

Defines the common behavior.

class Talkable {
public:
    virtual void talk() = 0;
};

2. Concrete Strategies

Different implementations of that behavior.

class NormalTalk : public Talkable {
public:
    void talk() override {
        cout << "Talking normally";
    }
};

class NonTalk : public Talkable {
public:
    void talk() override {
        cout << "Cannot talk";
    }
};

3. Context

The class that uses the strategy.

Here:

class Robot {
    Talkable* talkBehavior;

public:
    void talk() {
        talkBehavior->talk();
    }
};

So the Robot doesn't implement talking itself.

It delegates:

Robot
  │
  │ talk()
  ↓
Talkable
  │
  ├── NormalTalk
  └── NonTalk

7. Robot Example

A cleaner implementation:

class Talkable {
public:
    virtual void talk() = 0;
    virtual ~Talkable() {}
};
class NormalTalk : public Talkable {
public:
    void talk() override {
        cout << "Talking normally\n";
    }
};

class NonTalk : public Talkable {
public:
    void talk() override {
        cout << "Cannot talk\n";
    }
};

Similarly:

class Walkable {
public:
    virtual void walk() = 0;
};
class NormalWalk : public Walkable {
public:
    void walk() override {
        cout << "Walking normally\n";
    }
};

class NonWalk : public Walkable {
public:
    void walk() override {
        cout << "Cannot walk\n";
    }
};

And:

class Flyable {
public:
    virtual void fly() = 0;
};

8. Robot as the Context

class Robot {
protected:
    Talkable* talkBehavior;
    Walkable* walkBehavior;
    Flyable* flyBehavior;

public:
    Robot(Talkable* t, Walkable* w, Flyable* f)
        : talkBehavior(t),
          walkBehavior(w),
          flyBehavior(f) {}

    void talk() {
        talkBehavior->talk();
    }

    void walk() {
        walkBehavior->walk();
    }

    void fly() {
        flyBehavior->fly();
    }
};

Now:

Robot* r = new Robot(
    new NormalTalk(),
    new NormalWalk(),
    new NonFly()
);

Another robot can simply use:

Robot* r2 = new Robot(
    new NonTalk(),
    new NormalWalk(),
    new NormalFly()
);

No new Robot subclass is required.


9. The Main Benefit

Without Strategy:

Behavior combination
        ↓
New subclass
        ↓
More inheritance
        ↓
Class explosion

With Strategy:

Behavior combination
        ↓
Choose appropriate strategies
        ↓
Compose them into object

So behavior can be changed independently.


10. Runtime Change

One major advantage of Strategy is that the behavior can potentially be changed at runtime.

For example:

robot.setTalkBehavior(new NormalTalk());

Later:

robot.setTalkBehavior(new NonTalk());

The Robot itself doesn't change.

Only its strategy changes.

This is why the definition says:

"They can be changed at runtime."


11. Why Strategy Solves the Original Problem

Original design

Robot
 ├── NormalRobot
 ├── FlyingRobot
 ├── TalkingRobot
 ├── TalkingFlyingRobot
 ├── NonTalkingFlyingRobot
 └── ...

The class hierarchy tries to represent every possible combination.

Strategy design

                 Robot
            /       |       \
           ↓        ↓        ↓
       Talkable  Walkable  Flyable
          ↓         ↓         ↓
       Normal     Normal     Normal
       Non        Non        Non

Each dimension of behavior is independent.

Therefore:

Adding a new way of talking does not require creating new classes for every possible walking/flying combination.


12. Important Design Principle

Strategy is an example of:

Composition over inheritance

Instead of saying:

"Robot IS a NormalTalkRobot"

we say:

"Robot HAS a TalkBehavior"

And that behavior can be:

NormalTalk
NonTalk
FutureTalkBehavior
...

13. When to Use Strategy

Use Strategy when:

  • You have multiple ways of performing the same behavior.
  • Behavior can vary independently from the main class.
  • You see many subclasses differing mainly in behavior.
  • You want to avoid a growing inheritance hierarchy.
  • You want to change behavior dynamically.
  • You want to add new behaviors without modifying the main class heavily.

Typical examples

Payment
 ├── CreditCardPayment
 ├── UpiPayment
 └── PayPalPayment
Sorting
 ├── QuickSort
 ├── MergeSort
 └── HeapSort
Navigation
 ├── CarRoute
 ├── WalkingRoute
 └── BikeRoute
Compression
 ├── ZipCompression
 ├── GzipCompression
 └── RarCompression

14. Strategy Pattern — Interview Summary

Problem

Too many subclasses are created because different classes need different combinations of behaviors.

Solution

Extract each varying behavior into its own strategy class and compose those strategies with the main class.

Structure

Context
   │
   ├── Strategy A
   ├── Strategy B
   └── Strategy C

Strategy
   ├── ConcreteStrategy1
   ├── ConcreteStrategy2
   └── ...

Key principle

Encapsulate what varies.

Main advantage

Behavior varies independently of the object using it.

Relationship

Inheritance  → IS-A
Composition  → HAS-A

One-line memory trick

"Don't create a new class for every behavior combination; extract behaviors into strategies and compose them."


Strategy Pattern in one picture

              ┌──────────────┐
              │    Robot     │
              │   (Context)  │
              └──────┬───────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
        ↓            ↓            ↓
   Talkable       Walkable      Flyable
   Strategy       Strategy      Strategy
        │            │            │
    ┌───┴───┐    ┌───┴───┐    ┌───┴───┐
    ↓       ↓    ↓       ↓    ↓       ↓
 Normal    Non Normal   Non Normal   Non
 Talk      Talk Walk    Walk Fly     Fly

Core takeaway: The problem isn't that inheritance itself is bad. The problem is using inheritance to represent independent, changing behaviors. Strategy separates those behaviors so they can be combined freely.