Skip to content

Writing elegant code using Strategy Pattern

Posted on:June 9, 2024 at 12:58 PM

Have you ever faced a situation where you needed to switch between different methods to solve a problem? For example, imagine you are building a payment system for an online store. You might want to allow users to pay with a credit card, PayPal, or UPI. How do you design your code so that it’s easy to add new payment methods in the future without changing much of the existing code? This is where the Strategy Pattern comes in handy!

What is the Strategy Pattern?

The Strategy Pattern is a way of organizing code to make it more flexible and adaptable. Its’s a behavioural design pattern which allows you to write interchangable code. In simpler terms, it’s like having a toolbox with different tools for different tasks. You can pick the right tool (or strategy) when you need it, and you can easily swap tools without changing the toolbox itself.

Before Using the Strategy Pattern

This is an example for using multiple if-else conditions to implement the payment integration written in swift. Each payment method might have its own set of conditions and logic scattered throughout the codebase. Adding a new payment method would require modifying existing code, increasing the risk of introducing bugs. This is often messy, hard to maintain and hard to extend code.

// Handling payments without the Strategy Pattern
func processPayment(amount: Double, paymentMethod: String) -> String {
    var result = ""
    if paymentMethod == "CreditCard" {
        // Credit card payment processing logic here
        result = "Processing credit card payment of \(amount) dollars"
    } else if paymentMethod == "PayPal" {
        // PayPal payment processing logic here
        result = "Processing PayPal payment of \(amount) dollars"
    } else if paymentMethod == "UPI" {
        // UPI payment processing logic here
        result = "Processing UPI payment of \(amount) dollars"
    }
    return result
}

Let’s see how this code can be modified to use Strategy Pattern:

1. Define the Strategy Protocol

First, we need a common protocol that all payment methods will conform to.

protocol PaymentStrategy {
    func processPayment(amount: Double) -> String
}

2. Implement Concrete Strategies

Next, we create classes for each payment method, conforming to the PaymentStrategy protocol and implementing the processPayment method.

class CreditCardPayment: PaymentStrategy {
    func processPayment(amount: Double) -> String {
        // Credit card payment processing logic here
        return "Processing credit card payment of \(amount) dollars"
    }
}

class PayPalPayment: PaymentStrategy {
    func processPayment(amount: Double) -> String {
        // PayPal payment processing logic here
        return "Processing PayPal payment of \(amount) dollars"
    }
}

class UPIMobilePayment: PaymentStrategy {
    func processPayment(amount: Double) -> String {
        // UPI payment processing logic here
        return "Processing UPI payment of \(amount) dollars"
    }
}

3. Create the Context Class

The context class uses a strategy object. It allows changing the strategy at runtime.

class PaymentProcessor {
    var paymentStrategy: PaymentStrategy

    init(paymentStrategy: PaymentStrategy) {
        self.paymentStrategy = paymentStrategy
    }

    func setPaymentStrategy(paymentStrategy: PaymentStrategy) {
        self.paymentStrategy = paymentStrategy
    }

    func processPayment(amount: Double) -> String {
        return paymentStrategy.processPayment(amount: amount)
    }
}

4. Use the Strategy Pattern

Now, let’s see how we can use these classes to process payments.

let amount: Double = 100

let paymentProcessor = PaymentProcessor(paymentStrategy: CreditCardPayment())
print(paymentProcessor.processPayment(amount: amount)) // Outputs: Processing credit card payment of 100 dollars

paymentProcessor.setPaymentStrategy(paymentStrategy: PayPalPayment())
print(paymentProcessor.processPayment(amount: amount)) // Outputs: Processing PayPal payment of 100 dollars

paymentProcessor.setPaymentStrategy(paymentStrategy: UPIMobilePayment())
print(paymentProcessor.processPayment(amount: amount)) // Outputs: Processing UPI payment of 100 dollars

Before using the strategy pattern:

func processPayment(amount: Double, paymentMethod: String) -> String {
    var result = ""
    if paymentMethod == "CreditCard" {
        // Credit card payment processing logic here
        result = "Processing credit card payment of \(amount) dollars"
    } else if paymentMethod == "PayPal" {
        // PayPal payment processing logic here
        result = "Processing PayPal payment of \(amount) dollars"
    } else if paymentMethod == "UPI" {
        // UPI payment processing logic here
        result = "Processing UPI payment of \(amount) dollars"
    }
    return result
}

After using the strategy pattern:

protocol PaymentStrategy {
    func processPayment(amount: Double) -> String
}

class CreditCardPayment: PaymentStrategy {
    func processPayment(amount: Double) -> String {
        // Credit card payment processing logic here
        return "Processing credit card payment of \(amount) dollars"
    }
}

class PayPalPayment: PaymentStrategy {
    func processPayment(amount: Double) -> String {
        // PayPal payment processing logic here
        return "Processing PayPal payment of \(amount) dollars"
    }
}

class UPIMobilePayment: PaymentStrategy {
    func processPayment(amount: Double) -> String {
        // UPI payment processing logic here
        return "Processing UPI payment of \(amount) dollars"
    }
}

class PaymentProcessor {
    var paymentStrategy: PaymentStrategy

    init(paymentStrategy: PaymentStrategy) {
        self.paymentStrategy = paymentStrategy
    }

    func setPaymentStrategy(paymentStrategy: PaymentStrategy) {
        self.paymentStrategy = paymentStrategy
    }

    func processPayment(amount: Double) -> String {
        return paymentStrategy.processPayment(amount: amount)
    }
}

Why Use the Strategy Pattern?

When Not to Use the Strategy Pattern

While the Strategy Pattern is a powerful for certain scenarios, it is not always the best choice. Here are some points to consider when deciding not to use the Strategy Pattern:

In short, strategy Pattern is a powerful tool in the arsenal of software developers. By understanding and applying this pattern, you can write cleaner, more modular, and more maintainable code.