LLD
Machine Coding
Interview Course
Java โ€ข Interview Prep
Lesson 1 of 6

Strategy Pattern


What is it? (The Analogy)

Imagine you're going on a trip from your house to the airport. You have multiple ways to get there: you could drive your car, take a taxi, ride the bus, or call an Uber. The destination is the same -- the airport -- but the strategy for getting there is different.

Here's the key: YOU get to choose which transportation strategy to use, and you can even switch strategies mid-trip (maybe the bus breaks down and you call a taxi instead). The trip itself doesn't care HOW you travel -- it just needs you to get there.

The Strategy Pattern is exactly this: it lets you define a family of algorithms (strategies), put each one in its own class, and make them interchangeable. The object using the strategy can switch between them at runtime without knowing the details of how each one works.

In one line: Strategy Pattern = "I need to do something, but HOW I do it can vary and should be pluggable."


Why do we need it?

Picture this nightmare code for a navigation app:

java
1// BAD EXAMPLE -- all routing logic crammed into one place
2public class Navigator {
3
4    public void navigate(String from, String to, String mode) {
5        if (mode.equals("car")) {
6            System.out.println("Calculating fastest road route...");
7            System.out.println("Checking for traffic jams...");
8            System.out.println("Avoiding toll roads...");
9            // 50 more lines of car-specific logic...
10        }
11        else if (mode.equals("bus")) {
12            System.out.println("Finding nearest bus stop...");
13            System.out.println("Checking bus schedules...");
14            System.out.println("Calculating transfer points...");
15            // 50 more lines of bus-specific logic...
16        }
17        else if (mode.equals("bicycle")) {
18            System.out.println("Finding bike lanes...");
19            System.out.println("Avoiding highways...");
20            System.out.println("Checking elevation...");
21            // 50 more lines of bicycle-specific logic...
22        }
23        // What about walking? Scooter? Train? Helicopter?!
24        // This method grows FOREVER.
25    }
26}

Every new travel mode means modifying this huge method. Testing car logic means loading bus and bicycle logic too. It violates both SRP and OCP.

The pain: One giant method full of if-else branches. Adding a new mode risks breaking every existing mode. You can't test one strategy in isolation. And you can't swap strategies at runtime.


How it works -- Step by Step

  1. Identify the behavior that varies -- What algorithm or behavior has multiple versions? (Sorting, routing, pricing, compression, etc.)
  1. Define the Strategy interface -- Create an interface that declares the method(s) all strategies must implement.
  1. Create concrete strategy classes -- Each algorithm variant becomes its own class implementing the interface.
  1. Create the Context class -- This is the class that USES a strategy. It holds a reference to a Strategy interface (not a concrete class).
  1. Allow strategy swapping -- The context can accept a new strategy via its constructor or a setter method, enabling runtime switching.

Let's Build It Together

Let's build a Music Player with different audio equalizer modes (strategies for how music sounds).

java
1// ========================================
2// Step 1: Define the Strategy interface
3// Every equalizer mode must implement this
4// ========================================
5
6public interface EqualizerStrategy {
7    // Takes raw audio settings and applies the EQ
8    void applyEqualizer(String songName);
9
10    // Returns a description of what this mode does
11    String getModeName();
12}
java
1// ========================================
2// Step 2: Create concrete strategies
3// Each one is a different way to equalize audio
4// ========================================
5
6public class RockEqualizer implements EqualizerStrategy {
7    @Override
8    public void applyEqualizer(String songName) {
9        System.out.println("  [ROCK MODE] Boosting bass and treble for: " + songName);
10        System.out.println("  Bass: +8dB | Mids: -2dB | Treble: +6dB");
11        System.out.println("  Adding punch and brightness!");
12    }
13
14    @Override
15    public String getModeName() {
16        return "Rock";
17    }
18}
19
20public class JazzEqualizer implements EqualizerStrategy {
21    @Override
22    public void applyEqualizer(String songName) {
23        System.out.println("  [JAZZ MODE] Warming up mids for: " + songName);
24        System.out.println("  Bass: +3dB | Mids: +5dB | Treble: +2dB");
25        System.out.println("  Smooth and warm sound activated!");
26    }
27
28    @Override
29    public String getModeName() {
30        return "Jazz";
31    }
32}
33
34public class ElectronicEqualizer implements EqualizerStrategy {
35    @Override
36    public void applyEqualizer(String songName) {
37        System.out.println("  [ELECTRONIC MODE] Maximizing bass for: " + songName);
38        System.out.println("  Bass: +10dB | Mids: +0dB | Treble: +4dB");
39        System.out.println("  Floor-shaking bass enabled!");
40    }
41
42    @Override
43    public String getModeName() {
44        return "Electronic";
45    }
46}
47
48public class PodcastEqualizer implements EqualizerStrategy {
49    @Override
50    public void applyEqualizer(String songName) {
51        System.out.println("  [PODCAST MODE] Enhancing voice clarity for: " + songName);
52        System.out.println("  Bass: -3dB | Mids: +7dB | Treble: +1dB");
53        System.out.println("  Crystal clear voice mode!");
54    }
55
56    @Override
57    public String getModeName() {
58        return "Podcast";
59    }
60}
java
1// ========================================
2// Step 3: Create the Context class (the Music Player)
3// It USES a strategy but doesn't know the details
4// ========================================
5
6public class MusicPlayer {
7    private String currentSong;
8    private EqualizerStrategy equalizer;  // <-- Holds the current strategy
9
10    public MusicPlayer() {
11        // Default strategy
12        this.equalizer = new RockEqualizer();
13    }
14
15    // The KEY method: swap strategies at runtime!
16    public void setEqualizerMode(EqualizerStrategy newEqualizer) {
17        this.equalizer = newEqualizer;
18        System.out.println("\nSwitched to " + newEqualizer.getModeName() + " mode!");
19    }
20
21    public void play(String songName) {
22        this.currentSong = songName;
23        System.out.println("\nNow playing: " + songName);
24        // Delegate to whatever strategy is currently set
25        equalizer.applyEqualizer(songName);
26    }
27
28    public void showCurrentMode() {
29        System.out.println("Current EQ mode: " + equalizer.getModeName());
30    }
31}
java
1// ========================================
2// Step 4: Run it -- watch strategies swap at runtime!
3// ========================================
4
5public class MusicApp {
6    public static void main(String[] args) {
7        MusicPlayer player = new MusicPlayer();
8
9        // Play with default Rock equalizer
10        player.play("Bohemian Rhapsody");
11
12        // User switches to Jazz mode
13        player.setEqualizerMode(new JazzEqualizer());
14        player.play("Take Five");
15
16        // User switches to Electronic mode
17        player.setEqualizerMode(new ElectronicEqualizer());
18        player.play("Sandstorm");
19
20        // User switches to Podcast mode
21        player.setEqualizerMode(new PodcastEqualizer());
22        player.play("Science Podcast Episode 42");
23
24        // Check what mode we're in
25        player.showCurrentMode();
26    }
27}

Output:

java
1Now playing: Bohemian Rhapsody
2  [ROCK MODE] Boosting bass and treble for: Bohemian Rhapsody
3  Bass: +8dB | Mids: -2dB | Treble: +6dB
4  Adding punch and brightness!
5
6Switched to Jazz mode!
7
8Now playing: Take Five
9  [JAZZ MODE] Warming up mids for: Take Five
10  Bass: +3dB | Mids: +5dB | Treble: +2dB
11  Smooth and warm sound activated!
12...

Aha moment: The MusicPlayer never has a single if-else about equalizer types. It just calls equalizer.applyEqualizer() and trusts that whatever strategy is plugged in will do the right thing. Adding a "Classical" equalizer means writing ONE new class -- zero changes to existing code!


Visual Mental Model

java
1  +-------------------+         +---------------------+
2  |   MusicPlayer     |         | EqualizerStrategy   |
3  |   (Context)       | ------> |   (Interface)       |
4  |                   |  uses   |                     |
5  | - equalizer ------+         | + applyEqualizer()  |
6  | + play()          |         | + getModeName()     |
7  | + setEqualizer()  |         +---------------------+
8  +-------------------+                  /|\
9                                 ________|________
10                                |    |    |       |
11                                v    v    v       v
12                            Rock  Jazz  Elec.  Podcast
13                             EQ    EQ    EQ      EQ
14
15  The MusicPlayer delegates to whatever strategy is currently set.
16  Strategies can be swapped at runtime via setEqualizer().

Common Mistakes & Gotchas

  • Overusing Strategy for single-variant behavior -- If there's only ever one way to do something, don't create a strategy interface for it. You'll just add complexity for no benefit.
  • Putting strategy selection logic in the wrong place -- The context (MusicPlayer) shouldn't decide which strategy to use. That decision belongs to the client code or a factory.
  • Fat strategies that share tons of code -- If your strategies share 90% of their code, consider the Template Method pattern instead, or extract shared logic into a base class.
  • Forgetting that strategies should be stateless -- If possible, keep strategies stateless (no instance variables) so they can be shared safely across contexts.
  • Confusing Strategy with State -- Strategy is chosen by the CLIENT (user picks rock mode). State changes automatically based on INTERNAL conditions (like a vending machine going from idle to dispensing). We'll cover State pattern later!

Interview Tip

Strategy Pattern is one of the most commonly asked patterns in LLD interviews. When you see a problem with "different types of X" (payment methods, sorting algorithms, notification channels, validation rules), immediately think Strategy. Tell the interviewer: "I'd use the Strategy Pattern here so we can add new types without modifying existing code." Then show the interface + concrete implementations + context. Bonus points if you mention that it follows both SRP and OCP.


Quick Quiz

  1. You're building a file compression tool that supports ZIP, RAR, GZIP, and TAR formats. How would you model this with the Strategy Pattern?
  1. What's the difference between choosing a strategy at compile time vs runtime? Can you think of scenarios for each?
  1. A classmate suggests putting all equalizer logic in the MusicPlayer class using a switch statement. What are three specific problems with that approach?

Summary -- Key Takeaways

  • Strategy Pattern encapsulates algorithms behind an interface, making them interchangeable at runtime.
  • The Context (e.g., MusicPlayer) holds a reference to the Strategy interface, not concrete classes. It delegates work to whatever strategy is plugged in.
  • Adding new strategies = adding new classes. No existing code is modified (hello, OCP!).
  • Use it when you have multiple ways to do the same thing and want to switch between them easily (payment methods, sorting algorithms, EQ modes, etc.).