Design Patterns #1: Strategy Pattern
Replacing If-Else with better design

Fullstack Developer | CSS | JavaScript | React | Angular | Web3
Before starting learning strategy pattern, let's first understand the problem that this pattern solves.
Suppose we are building an e-commerce application, where users can pay using multiple methods:
Credit Card
UPI
Net banking
A straightforward implementation would look like this:
class PaymentService {
void processPayment(String paymentType, double amount) {
if(paymentType.equals("CARD")) {
System.out.println("Processing Card Payment");
}
else if(paymentType.equals("UPI")) {
System.out.println("Processing UPI Payment");
}
else if(paymentType.equals("NET_BANKING")) {
System.out.println("Processing Net Banking Payment");
}
}
}
This code looks completely fine initially but as soon as a new payment way is introduced we will have to update the processPayment functionality directly and that can introduce new bugs in the existing system.
This design violates:
Open Closed Principle
Hard to maintain and extend the functionalities
Now, we know the problem and Stragey Pattern solves this problem.
What is Strategy Pattern?
Strategy Pattern allows us to define multiple algorithms or behaviors separately and choose one of them at runtime.
In simple words:
We define different classes for different behaviours and choose it at runtime instead of defining multiple if-else conditions.
Solution
First we need to create an abstraction.
interface PaymentStrategy {
void pay(double amount);
}
Now we can create different implementation for each type of methods.
Card Payment
class CardPaymentStrategy implements PaymentStrategy {
public void pay(double amount) {
System.out.printf("Paid %d using Card", amount);
}
}
UPI Payment
class UpiPaymentStrategy implements PaymentStrategy {
public void pay(double amount) {
System.out.printf("Paid %d using UPI", amount);
}
}
Netbanking Payment
class NetBankingPaymentStrategy implements PaymentStrategy {
public void pay(double amount) {
System.out.printf("Paid %d using Netbanking", amount);
}
}
Payment Service class
class PaymentService {
private PaymentStrategy paymentStrategy;
PaymentService(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
void processPayment(double amount) {
paymentStrategy.pay(amount);
}
}
Usage
// Using card for payment
PaymentService cardPaymentService = new PaymentService(new CardPaymentStrategy());
cardPaymentService.processPayment(1000);
// Using Upi for payment
PaymentService upiPaymentService = new PaymentService(new UpiPaymentStrategy());
upiPaymentService.processPayment(1000);
// same goes for other methods
As you can see it's decided at runtime, what method to be used for the payment and then picks the correct strategy to process the payment.
This is known as Strategy Pattern because it picks different strategy for different cases and if a new case is introduced just introducing a new stratgey would solve this wothout violating OCP.
This pattern uses OCP and DIP and also composition is used instead of inheritcan and Program to Intefaces instead of concrete classes.
Golden Rule
Whenever you see code like:
if(type.equals(...))
else if(type.equals(...))
else if(type.equals(...))
and new conditions keep getting added over time,
ask yourself:
Can each branch become its own class?
If yes, Strategy Pattern is often a good candidate.



