Home/Design Patterns/Strategy Pattern

Strategy Pattern

🎯 Explain Like I'm 5...

Imagine you want to get to your friend's house for a playdate! You can choose HOW to get there - each way is a different 'strategy'! 🏠

😟 The Problem:

  • Your friend lives 2 miles away
  • You could walk, bike, have mom drive you, or even fly with a jetpack!
  • Which way is best depends on: weather, time, how tired you are, how cool you want to look! 😎

💡 The Solution - Pick Your Strategy!

  • Sunny day + have time? WALK! 🚶
  • Want exercise + faster? BIKE! 🚴
  • Raining + far away? CAR! 🚗
  • Want to be super cool? JETPACK! 🚀 (okay, maybe not...)

🌟 The Key Idea:

You're doing the SAME thing (getting to friend's house), but you can CHOOSE the METHOD! Each method is a 'strategy' that does the job differently. You can easily SWITCH strategies based on what works best!

🚀 Why Is This Pattern Useful?

  • Swap algorithms easily without changing your main code!
  • Add new strategies without touching existing ones!
  • Choose the best method at RUNTIME based on conditions!
  • Keep your code clean - each strategy lives in its own class!

📋 Pattern Purpose

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. Instead of implementing a single algorithm directly, the code receives runtime instructions about which algorithm to use from a family of algorithms.

⚡ When to Use Strategy Pattern

  • You have multiple algorithms for a specific task and want to switch between them
  • You want to avoid multiple conditional statements (if/else, switch) for selecting algorithms
  • Related algorithms have different implementations but similar interfaces
  • You need to hide complex algorithm-specific data from clients
  • You want to change behavior at runtime

🧩 Strategy Pattern Components

Strategy Interface:

Declares the common interface for all concrete strategies

Concrete Strategy:

Implements the algorithm using the Strategy interface

Context:

Maintains a reference to a Strategy object and uses it to call the algorithm

💻 Java Implementations

Example 1: Payment Methods

Different payment strategies (CreditCard, PayPal, Bitcoin) that can be swapped at runtime.

PaymentStrategy.java
java
1
2
3
4
5
// Strategy Interface - defines the algorithm contract
public interface PaymentStrategy {
void pay(double amount);
String getPaymentType();
}
CreditCardStrategy.java
java
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
// Concrete Strategy 1 - Credit Card Payment
public class CreditCardStrategy implements PaymentStrategy {
private String cardNumber;
private String cvv;
private String expiryDate;
public CreditCardStrategy(String cardNumber, String cvv, String expiryDate) {
this.cardNumber = cardNumber;
this.cvv = cvv;
this.expiryDate = expiryDate;
}
@Override
public void pay(double amount) {
System.out.println("Paying $" + amount + " using Credit Card");
System.out.println("Card Number: " + maskCardNumber(cardNumber));
System.out.println("Processing payment...");
// Payment processing logic here
}
@Override
public String getPaymentType() {
return "Credit Card";
}
private String maskCardNumber(String cardNumber) {
return "**** **** **** " + cardNumber.substring(cardNumber.length() - 4);
}
}
PayPalStrategy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Concrete Strategy 2 - PayPal Payment
public class PayPalStrategy implements PaymentStrategy {
private String email;
private String password;
public PayPalStrategy(String email, String password) {
this.email = email;
this.password = password;
}
@Override
public void pay(double amount) {
System.out.println("Paying $" + amount + " using PayPal");
System.out.println("Email: " + email);
System.out.println("Authenticating...");
// PayPal API integration here
}
@Override
public String getPaymentType() {
return "PayPal";
}
}
BitcoinStrategy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Concrete Strategy 3 - Bitcoin Payment
public class BitcoinStrategy implements PaymentStrategy {
private String walletAddress;
public BitcoinStrategy(String walletAddress) {
this.walletAddress = walletAddress;
}
@Override
public void pay(double amount) {
System.out.println("Paying $" + amount + " using Bitcoin");
System.out.println("Wallet: " + walletAddress);
System.out.println("Converting to BTC...");
System.out.println("Broadcasting transaction to blockchain...");
// Bitcoin transaction logic here
}
@Override
public String getPaymentType() {
return "Bitcoin";
}
}
ShoppingCart.java
java
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
// Context - uses a PaymentStrategy
import java.util.ArrayList;
import java.util.List;
public class ShoppingCart {
private List<Item> items;
private PaymentStrategy paymentStrategy;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Item item) {
items.add(item);
}
public double calculateTotal() {
return items.stream()
.mapToDouble(Item::getPrice)
.sum();
}
// Strategy can be set at runtime!
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout() {
if (paymentStrategy == null) {
System.out.println("Please select a payment method!");
return;
}
double total = calculateTotal();
System.out.println("\n=== Checkout ===");
System.out.println("Total: $" + total);
System.out.println("Payment Method: " + paymentStrategy.getPaymentType());
paymentStrategy.pay(total);
System.out.println("Payment successful!\n");
}
}
class Item {
private String name;
private double price;
public Item(String name, double price) {
this.name = name;
this.price = price;
}
public double getPrice() {
return price;
}
}
PaymentDemo.java
java
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
// Client code demonstrating runtime strategy switching
public class PaymentDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// Add items to cart
cart.addItem(new Item("Laptop", 999.99));
cart.addItem(new Item("Mouse", 29.99));
cart.addItem(new Item("Keyboard", 79.99));
// Scenario 1: Pay with Credit Card
System.out.println("Customer chooses Credit Card:");
cart.setPaymentStrategy(new CreditCardStrategy(
"1234567890123456", "123", "12/25"
));
cart.checkout();
// Scenario 2: Pay with PayPal
System.out.println("Customer chooses PayPal:");
cart.setPaymentStrategy(new PayPalStrategy(
"customer@email.com", "password123"
));
cart.checkout();
// Scenario 3: Pay with Bitcoin
System.out.println("Customer chooses Bitcoin:");
cart.setPaymentStrategy(new BitcoinStrategy(
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
));
cart.checkout();
// The cart works with ANY payment strategy!
// We can easily add more payment methods without changing ShoppingCart!
}
}

Example 2: Sorting Algorithms

Different sorting strategies (BubbleSort, QuickSort, MergeSort) for different data sizes.

SortStrategy.java
java
1
2
3
4
5
// Strategy Interface
public interface SortStrategy {
void sort(int[] array);
String getName();
}
BubbleSortStrategy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Concrete Strategy 1 - Good for small datasets
public class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("Sorting using Bubble Sort (O())");
int n = array.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (array[j] > array[j + 1]) {
// Swap
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
@Override
public String getName() {
return "Bubble Sort";
}
}
QuickSortStrategy.java
java
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
// Concrete Strategy 2 - Good for general use
public class QuickSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("Sorting using Quick Sort (O(n log n))");
quickSort(array, 0, array.length - 1);
}
private void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
@Override
public String getName() {
return "Quick Sort";
}
}
MergeSortStrategy.java
java
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
// Concrete Strategy 3 - Best for large datasets
public class MergeSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("Sorting using Merge Sort (O(n log n))");
mergeSort(array, 0, array.length - 1);
}
private void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
private void merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int[] L = new int[n1];
int[] R = new int[n2];
System.arraycopy(arr, left, L, 0, n1);
System.arraycopy(arr, mid + 1, R, 0, n2);
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k++] = L[i++];
} else {
arr[k++] = R[j++];
}
}
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}
@Override
public String getName() {
return "Merge Sort";
}
}
DataSorter.java
java
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
// Context - chooses sorting strategy based on data size
import java.util.Arrays;
public class DataSorter {
private SortStrategy strategy;
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void performSort(int[] data) {
if (strategy == null) {
System.out.println("No sorting strategy selected!");
return;
}
System.out.println("Before: " + Arrays.toString(data));
long startTime = System.nanoTime();
strategy.sort(data);
long endTime = System.nanoTime();
System.out.println("After: " + Arrays.toString(data));
System.out.println("Time taken: " + (endTime - startTime) / 1_000_000.0 + " ms\n");
}
// Smart selection based on array size
public void autoSelectStrategy(int arraySize) {
if (arraySize < 10) {
setStrategy(new BubbleSortStrategy());
System.out.println("Auto-selected: Bubble Sort (small dataset)");
} else if (arraySize < 1000) {
setStrategy(new QuickSortStrategy());
System.out.println("Auto-selected: Quick Sort (medium dataset)");
} else {
setStrategy(new MergeSortStrategy());
System.out.println("Auto-selected: Merge Sort (large dataset)");
}
}
public static void main(String[] args) {
DataSorter sorter = new DataSorter();
// Small dataset
int[] smallData = {64, 34, 25, 12, 22, 11, 90};
sorter.autoSelectStrategy(smallData.length);
sorter.performSort(smallData);
// Medium dataset
int[] mediumData = {5, 2, 9, 1, 7, 6, 3};
sorter.autoSelectStrategy(mediumData.length);
sorter.performSort(mediumData);
// Manual strategy selection
int[] customData = {3, 7, 1, 9, 2};
System.out.println("Manual selection: Merge Sort");
sorter.setStrategy(new MergeSortStrategy());
sorter.performSort(customData);
}
}

Example 3: Navigation Strategies

Different travel strategies (Walk, Bike, Car) based on distance and conditions.

NavigationStrategy.java
java
1
2
3
4
5
6
// Strategy Interface
public interface NavigationStrategy {
void navigate(String from, String to);
double estimateTime(double distance);
String getMode();
}
WalkStrategy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Concrete Strategy 1 - Walking
public class WalkStrategy implements NavigationStrategy {
private static final double WALK_SPEED = 5.0; // km/h
@Override
public void navigate(String from, String to) {
System.out.println("🚶 Walking directions from " + from + " to " + to);
System.out.println("- Take the pedestrian path");
System.out.println("- Use crosswalks");
System.out.println("- Enjoy the scenery!");
}
@Override
public double estimateTime(double distance) {
return (distance / WALK_SPEED) * 60; // minutes
}
@Override
public String getMode() {
return "Walking";
}
}
BikeStrategy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Concrete Strategy 2 - Biking
public class BikeStrategy implements NavigationStrategy {
private static final double BIKE_SPEED = 15.0; // km/h
@Override
public void navigate(String from, String to) {
System.out.println("🚴 Biking directions from " + from + " to " + to);
System.out.println("- Follow bike lanes when available");
System.out.println("- Watch for traffic");
System.out.println("- Use bike-friendly routes");
}
@Override
public double estimateTime(double distance) {
return (distance / BIKE_SPEED) * 60; // minutes
}
@Override
public String getMode() {
return "Biking";
}
}
CarStrategy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Concrete Strategy 3 - Driving
public class CarStrategy implements NavigationStrategy {
private static final double CAR_SPEED = 50.0; // km/h (city average)
@Override
public void navigate(String from, String to) {
System.out.println("🚗 Driving directions from " + from + " to " + to);
System.out.println("- Take main roads");
System.out.println("- Follow traffic signals");
System.out.println("- Watch for speed limits");
System.out.println("- Parking may be required");
}
@Override
public double estimateTime(double distance) {
// Add 10 minutes for traffic and parking
return (distance / CAR_SPEED) * 60 + 10; // minutes
}
@Override
public String getMode() {
return "Driving";
}
}
Navigator.java
java
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
63
64
65
66
67
68
69
// Context - Navigation system
public class Navigator {
private NavigationStrategy strategy;
private String currentLocation;
public Navigator(String currentLocation) {
this.currentLocation = currentLocation;
}
public void setStrategy(NavigationStrategy strategy) {
this.strategy = strategy;
}
public void routeTo(String destination, double distance) {
if (strategy == null) {
System.out.println("Please select a navigation mode!");
return;
}
System.out.println("\n=== Navigation ===");
System.out.println("Mode: " + strategy.getMode());
System.out.println("Distance: " + distance + " km");
double time = strategy.estimateTime(distance);
System.out.println("Estimated time: " + String.format("%.1f", time) + " minutes");
System.out.println();
strategy.navigate(currentLocation, destination);
System.out.println("==================\n");
}
// Smart strategy selection based on conditions
public void autoSelectStrategy(double distance, boolean isRaining, boolean hasTime) {
if (distance < 1.0 && hasTime) {
setStrategy(new WalkStrategy());
System.out.println("Auto-selected: Walking (short distance)");
} else if (distance < 5.0 && !isRaining) {
setStrategy(new BikeStrategy());
System.out.println("Auto-selected: Biking (good weather, medium distance)");
} else {
setStrategy(new CarStrategy());
System.out.println("Auto-selected: Driving (long distance or bad weather)");
}
}
public static void main(String[] args) {
Navigator nav = new Navigator("Home");
// Scenario 1: Short trip, nice weather
System.out.println("Scenario 1: Going to nearby park (0.8 km)");
nav.autoSelectStrategy(0.8, false, true);
nav.routeTo("Central Park", 0.8);
// Scenario 2: Medium distance, rainy
System.out.println("Scenario 2: Going to work (4 km), raining");
nav.autoSelectStrategy(4.0, true, true);
nav.routeTo("Office", 4.0);
// Scenario 3: Long distance
System.out.println("Scenario 3: Going to mall (12 km)");
nav.autoSelectStrategy(12.0, false, true);
nav.routeTo("Shopping Mall", 12.0);
// Manual override
System.out.println("Scenario 4: User manually chooses to bike");
nav.setStrategy(new BikeStrategy());
nav.routeTo("Coffee Shop", 3.0);
}
}

🌍 Real-World Examples

  • 💳Payment Processing: Credit card, PayPal, cryptocurrency, bank transfer
  • 🗜️Compression Algorithms: ZIP, RAR, 7Z based on file type and size
  • 🗺️Route Planning: Walking, driving, public transit, cycling directions
  • 📝Logging: Console logging, file logging, database logging, cloud logging
  • 🖼️Image Rendering: PNG, JPEG, WebP, SVG based on quality and size needs
  • Data Validation: Email, phone, credit card, password validators

✅ Benefits

  • Open/Closed Principle: Easy to add new strategies without modifying context
  • Single Responsibility: Each strategy class has one job
  • Runtime Flexibility: Switch strategies dynamically based on conditions
  • Eliminates Conditionals: Replace complex if/else chains with strategy objects
  • Testing: Each strategy can be tested independently
  • Code Reuse: Strategies can be reused across different contexts

⚠️ Drawbacks

  • ⚠️More Classes: Creates many strategy classes for simple cases
  • ⚠️Complexity: Can be overkill if you only have 2-3 algorithms that rarely change
  • ⚠️Client Awareness: Clients must know about different strategies to choose one
  • ⚠️Communication Overhead: Context and strategies must share data somehow

🔑 Key Points to Remember

  • 1️⃣Strategy encapsulates algorithms into separate classes with a common interface
  • 2️⃣Context delegates work to a strategy object instead of implementing multiple variants
  • 3️⃣Strategies are INTERCHANGEABLE - context doesn't care which one is used
  • 4️⃣Use Strategy when you have many related classes that differ only in behavior
  • 5️⃣Different from State pattern - Strategy focuses on algorithm selection, State on object state transitions

💪 Practice Scenarios

  • Build a text formatting system with strategies: UpperCase, LowerCase, TitleCase, CamelCase
  • Create a discount calculator with strategies: PercentageDiscount, FixedAmountDiscount, BuyOneGetOne
  • Implement a data export system with strategies: CSVExporter, JSONExporter, XMLExporter, PDFExporter
  • Design a shipping cost calculator: StandardShipping, ExpressShipping, OvernightShipping, InternationalShipping
  • Create a notification system: EmailNotification, SMSNotification, PushNotification, SlackNotification