Skip to main content

Command Palette

Search for a command to run...

Design Patterns #1: Strategy Pattern

Replacing If-Else with better design

Updated
3 min read
Design Patterns #1: Strategy Pattern
A

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:

  1. Credit Card

  2. UPI

  3. 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.