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.