LLD - Strategy Design Pattern
Understanding Strategy Design Pattern
This behavioral design pattern enables the selection of an algorithm’s implementation at runtime.
Problem it Solves: When you have multiple variations of an algorithm or behavior, embedding them into a single class using conditionals (if-else
or switch-case
) leads to rigid and unmaintainable code. The Strategy pattern addresses this by allowing behaviors to be selected dynamically.
Easy Explanation: Exam Preparation Strategy
In college, your exam preparation strategy varied a lot depending on how far away the date of the exam was.
Strategies:
2 months before the exam: Study casually, covering the basics
1 month before the exam: Study in more detail for a deeper understanding
1 week before the exam: Focus on important topics and take practice tests
1 day before the exam: Quick revision and review of previous year's questions
Blueprint of Strategy Design Pattern:
Strategy Interface: Define an interface that represents the strategy contract.
Concrete Strategies: Implement concrete classes that implement the strategy interface. Each concrete strategy represents a specific algorithm or behavior.
Context Class: Create a context class that holds a reference to the current strategy object. This class interacts with the strategy objects and delegates tasks to them.
Client Code: Use the context class to select and execute a specific strategy at runtime.
Code:
// Exam Preparation Strategy Interface
public interface ExamStrategy {
void prepare();
}
// 2 Months Before Exam Strategy
public class CasualStudyStrategy implements ExamStrategy {
@Override
public void prepare() {
System.out.println("2 months before exam: Casual Study");
}
}
// 1 Month Before Exam Strategy
public class FocusedStudyStrategy implements ExamStrategy {
@Override
public void prepare() {
System.out.println("1 Month Before Exam: Focused Study");
}
}
// 1 Week Before Exam Strategy
public class IntensiveStudyStrategy implements ExamStrategy {
@Override
public void prepare() {
System.out.println("1 Week Before Exam: Intensive Study");
}
}
// 1 Day Before Exam Strategy
public class LastMinuteRevisionStrategy implements ExamStrategy {
@Override
public void prepare() {
System.out.println("1 Day Before Exam: Last Minute Revision");
}
}
// Student (Context)
public class Student {
private ExamStrategy examStrategy;
// Set the strategy dynamically
public void setExamStrategy(ExamStrategy strategy) {
this.examStrategy = strategy;
}
// Perform preparation based on the selected strategy
public void prepareForExam() {
examStrategy.prepare();
}
}
// Client Code
public class ExamPreparationDemo {
public static void main(String[] args) {
Student student = new Student();
// 2 months before the exam
student.setExamStrategy(new CasualStudyStrategy());
student.prepareForExam();
// 1 month before the exam
student.setExamStrategy(new FocusedStudyStrategy());
student.prepareForExam();
// 1 week before the exam
student.setExamStrategy(new IntensiveStudyStrategy());
student.prepareForExam();
// 1 day before the exam
student.setExamStrategy(new LastMinuteRevisionStrategy());
student.prepareForExam();
}
}
Here are a few other examples to build intuition around Strategy Design Patterns
Navigation Strategy:
Google Maps supports different navigation options like Driving, Walking or Cycling
// Navigation Strategy Interface
public interface NavigationStrategy {
void navigate(String start, String end);
}
// Driving Route Strategy
public class DrivingRoute implements NavigationStrategy {
@Override
public void navigate(String start, String end) {
System.out.println("Calculating driving route from " + start + " to " + end);
}
}
// Walking Route Strategy
public class WalkingRoute implements NavigationStrategy {
@Override
public void navigate(String start, String end) {
System.out.println("Calculating walking route from " + start + " to " + end);
}
}
// Cycling Route Strategy
public class CyclingRoute implements NavigationStrategy {
@Override
public void navigate(String start, String end) {
System.out.println("Calculating cycling route from " + start + " to " + end);
}
}
// Navigation App (Context)
public class NavigationApp {
private NavigationStrategy navigationStrategy;
public void setNavigationStrategy(NavigationStrategy strategy) {
this.navigationStrategy = strategy;
}
public void navigate(String start, String end) {
navigationStrategy.navigate(start, end);
}
}
// Client Code
public class NavigationDemo {
public static void main(String[] args) {
NavigationApp app = new NavigationApp();
// Driving Strategy
app.setNavigationStrategy(new DrivingRoute());
app.navigate("Home", "Office");
// Walking Strategy
app.setNavigationStrategy(new WalkingRoute());
app.navigate("Home", "Park");
// Cycling Strategy
app.setNavigationStrategy(new CyclingRoute());
app.navigate("Home", "Gym");
}
}
Payment Strategy:
A payment system that supports multiple payment options like - Credit Card Payment, Paypal Payment, UPI Payment, Crypto Payment, etc
// Payment Strategy Interface
public interface PaymentStrategy {
void pay(int amount);
}
// Credit Card Payment Strategy
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card " + cardNumber);
}
}
// PayPal Payment Strategy
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal account: " + email);
}
}
// UPI Payment Strategy
public class UPIPayment implements PaymentStrategy {
private String upiId;
public UPIPayment(String upiId) {
this.upiId = upiId;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using UPI ID: " + upiId);
}
}
// Checkout System (Context)
public class CheckoutSystem {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}
// Client Code
public class PaymentDemo {
public static void main(String[] args) {
CheckoutSystem checkout = new CheckoutSystem();
// Pay via Credit Card
checkout.setPaymentStrategy(new CreditCardPayment("1234-5678-9876"));
checkout.checkout(200);
// Switch to PayPal
checkout.setPaymentStrategy(new PayPalPayment("user@example.com"));
checkout.checkout(150);
// Switch to UPI
checkout.setPaymentStrategy(new UPIPayment("user@upi"));
checkout.checkout(300);
}
}
Compression Strategy:
A system that supports different file compression strategies like Zip, RAR, 7z, etc
// Compression Strategy Interface
public interface CompressionStrategy {
void compress(String fileName);
}
// ZIP Compression Strategy
public class ZIPCompression implements CompressionStrategy {
@Override
public void compress(String fileName) {
System.out.println("Compressing " + fileName + " using ZIP format");
}
}
// RAR Compression Strategy
public class RARCompression implements CompressionStrategy {
@Override
public void compress(String fileName) {
System.out.println("Compressing " + fileName + " using RAR format");
}
}
// 7z Compression Strategy
public class SevenZCompression implements CompressionStrategy {
@Override
public void compress(String fileName) {
System.out.println("Compressing " + fileName + " using 7z format");
}
}
// Compression Utility (Context)
public class CompressionUtility {
private CompressionStrategy compressionStrategy;
public void setCompressionStrategy(CompressionStrategy strategy) {
this.compressionStrategy = strategy;
}
public void compressFile(String fileName) {
compressionStrategy.compress(fileName);
}
}
// Client Code (Main Method)
public class CompressionDemo {
public static void main(String[] args) {
CompressionUtility utility = new CompressionUtility();
// Using ZIP compression
utility.setCompressionStrategy(new ZIPCompression());
utility.compressFile("file1.txt");
// Switch to RAR compression
utility.setCompressionStrategy(new RARCompression());
utility.compressFile("file2.txt");
// Switch to 7z compression
utility.setCompressionStrategy(new SevenZCompression());
utility.compressFile("file3.txt");
}
}
Text Formatting Strategy:
Text Editor supports different text formatting options like Bold, Italic, and Underline.
// Text Formatting Strategy Interface
public interface TextFormattingStrategy {
String format(String text);
}
// Bold Text Formatting Strategy
public class BoldTextStrategy implements TextFormattingStrategy {
@Override
public String format(String text) {
return "**" + text + "**"; // Markdown-style bold
}
}
// Italic Text Formatting Strategy
public class ItalicTextStrategy implements TextFormattingStrategy {
@Override
public String format(String text) {
return "*" + text + "*"; // Markdown-style italic
}
}
// Underline Text Formatting Strategy
public class UnderlineTextStrategy implements TextFormattingStrategy {
@Override
public String format(String text) {
return "__" + text + "__"; // Markdown-style underline
}
}
// Text Editor (Context)
public class TextEditor {
private TextFormattingStrategy formattingStrategy;
public void setFormattingStrategy(TextFormattingStrategy strategy) {
this.formattingStrategy = strategy;
}
public String formatText(String text) {
return formattingStrategy.format(text);
}
}
// Client Code
public class TextEditorDemo {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
// Bold Text Strategy
editor.setFormattingStrategy(new BoldTextStrategy());
System.out.println(editor.formatText("Hello World!")); // Output: **Hello World!**
// Italic Text Strategy
editor.setFormattingStrategy(new ItalicTextStrategy());
System.out.println(editor.formatText("Hello World!")); // Output: *Hello World!*
// Underline Text Strategy
editor.setFormattingStrategy(new UnderlineTextStrategy());
System.out.println(editor.formatText("Hello World!")); // Output: __Hello World!__
}
}
Benefits:
OCP: Strategy pattern promotes the Open/Closed Principles as new algorithms or strategies can be added without modifying the existing code
Flexibility: It provides flexibility to switch strategies dynamically at run time
Avoids Conditional Logic: It helps to avoid multiple conditionals like if-else or switch by encapsulating behaviors in separate classes.
Subscribe to my newsletter
Read articles from Manish Pushkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Manish Pushkar
Manish Pushkar
Software Engineer - Backend