TeachingBee

Strategy Design Pattern : Low Level System Design

strategy design pattern

The Strategy Design Pattern is a behavioral pattern that enables selecting an algorithm’s runtime behaviour among a family of algorithms. It encapsulates each algorithm, allowing them to be interchangeable within that family.

The key objective is to separate the concerns of how a certain task is performed from the context in which it is used, promoting a more modular and flexible design.

What is Strategy Design Pattern?


The Strategy Design Pattern is a behavioral software design pattern that enables selecting an algorithm’s behavior at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

Consider an application that processes payments. Initially, you might support only one payment method, such as credit card payments. However, as the application grows, you need to support additional payment methods like PayPal, bank transfers, or cryptocurrencies.

Without the Strategy Pattern, you might find yourself adding multiple conditional statements throughout your code to handle each payment method differently, leading to a system that’s hard to maintain and extend.

class PaymentProcessor {
    void processPayment(double amount, String method) {
        if (method.equals("CreditCard")) {
            // Process credit card payment
        } else if (method.equals("PayPal")) {
            // Process PayPal payment
        }
        // More conditions for other payment methods
    }
}

This approach quickly becomes unwieldy and violates the Open/Closed Principle as new payment methods require modifications to the existing PaymentProcessor class.

Solving the Issue with Strategy Design Pattern

The Strategy Pattern addresses this issue by defining a family of algorithms (in this case, payment methods), encapsulating each one, and making them interchangeable. The payment processing strategy can vary independently from clients that use it.

How to Implement Strategy Design Pattern?

Strategy Design Pattern

Strategy Design Pattern can be implemented by using following components:

  1. Strategy Interface: This defines a common interface for all supported algorithms. Each algorithm encapsulated by the strategy pattern adheres to this interface, ensuring they are interchangeable within the context where they are used.
  2. Concrete Strategies: These are specific implementations of the strategy interface. Each concrete strategy implements an algorithm, and the context can switch between them at runtime depending on the situation.
  3. Context: This is the class that contains a reference to a strategy object. The context doesn’t perform algorithmic work by itself; instead, it delegates that work to the strategy object. The context is not responsible for which algorithm is chosen; it just knows how to execute the algorithm encapsulated within the strategy.

Step-by-Step Implementation

Strategy Design Pattern Example
  1. Define the Strategy Interface:
INTERFACE PaymentStrategy
    METHOD processPayment(amount: DOUBLE)
END INTERFACE
  1. Implement Concrete Strategies:
CLASS CreditCardPayment IMPLEMENTS PaymentStrategy
    METHOD processPayment(amount: DOUBLE)
        PRINT "Processing credit card payment of $" + amount
    END METHOD
END CLASS

CLASS PayPalPayment IMPLEMENTS PaymentStrategy
    METHOD processPayment(amount: DOUBLE)
        PRINT "Processing PayPal payment of $" + amount
    END METHOD
END CLASS
  1. Create the Context Class:
CLASS PaymentProcessor
    PRIVATE paymentStrategy: PaymentStrategy

    CONSTRUCTOR(PaymentProcessor, paymentStrategy: PaymentStrategy)
        this.paymentStrategy = paymentStrategy
    END CONSTRUCTOR

    METHOD process(amount: DOUBLE)
        paymentStrategy.processPayment(amount)
    END METHOD
END CLASS
  1. Usage:
// Create a PaymentProcessor for credit card payments
   creditCardPayment: PaymentProcessor = NEW PaymentProcessor(NEW CreditCardPayment())
   creditCardPayment.process(100.0)
        
// Create a PaymentProcessor for PayPal payments
        
    payPalPayment: PaymentProcessor = NEW PaymentProcessor(NEW         PayPalPayment())
    payPalPayment.process(200.0)

Now the above code by using Strategy pattern solved various issues:

  • Encapsulation and Separation of Concerns: The Strategy pattern encapsulates each payment method into its own class, separating the implementation details of each strategy from the client code.
  • Flexibility and Extensibility: It allows new payment methods to be added easily by creating new concrete strategy classes without modifying the existing code. We can simply create a new payment strategy class that implements the PaymentStrategy interface and pass it to the PaymentProcessor.
  • Reduced Coupling: The client code (Main class) is decoupled from the implementation details of payment processing strategies. It only interacts with the PaymentProcessor class, which takes care of invoking the appropriate strategy. This promotes code maintainability and scalability.

Common Usage of the Strategy Design Pattern

The Strategy Pattern is commonly used in:

  1. Sorting and Searching Algorithms: Where different algorithms might be preferred based on the context of the data.
  2. Payment Processing Systems: To support various payment methods without changing the system’s core logic.
  3. Compression Tools: Where different compression algorithms can be chosen based on user preference or data type.
  4. Navigational Systems: Where different routing algorithms (fastest, shortest, no-toll, etc.) can be selected dynamically.

Identifying Where To Apply Strategy Design Pattern

  • Multiple Algorithms for a Task: When there are several ways to do a task, and the best method varies depending on circumstances.
  • High Cohesion: When you have a class that performs a variety of actions in different ways depending on some conditions.
  • **Runtime Configuration:** When the behavior of an application needs to be selected at runtime based on user input or configuration.
  • Avoiding Conditional Statements: To eliminate complex conditional logic that selects between different variants of the same algorithm.

Benefits of the Strategy Design Pattern

  1. Flexibility: Allows for the dynamic changing of behavior within an object.
  2. Decoupling: Strategies and the context are decoupled, promoting loose coupling.
  3. Open/Closed Principle: New strategies can be introduced without changing the context, adhering to the Open/Closed Principle.
  4. Reusable: Strategies can be reused across different contexts.

DrawBacks of the Strategy Design Pattern

  1. Complexity: Introduces additional classes and interfaces, which can increase the complexity of the code.
  2. Clients Must Be Aware of Strategies: Clients need to know about the different strategies to choose the appropriate one.
  3. Increased Number of Objects: Every strategy is typically implemented as a separate object, which can lead to a proliferation of objects in the system.

Key TakeAways

  • Strategy pattern decouples the implementation details of algorithms from the clients that use them. This means the client code (the context) does not need to change when a new algorithm (strategy) is added or one is modified. This separation of concerns enhances modularity and makes the system easier to understand, maintain, and extend.
  • By encapsulating the algorithm behavior within separate strategy classes, the Strategy pattern allows the behavior of a class to be changed at runtime by substituting different strategy objects. This flexibility is particularly useful for applications that require dynamically changing behavior based on user input, configuration, or other conditions.
  • The pattern aligns with the Open/Closed Principle, one of the SOLID principles of object-oriented design, which states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. You can introduce new strategies without altering the context or other strategies, making the system extensible.
  • Each strategy can be tested independently of the context class and other strategy classes. This separation makes unit testing easier, as you can focus on testing the behavior of individual strategies without concern for their interaction with the system as a whole.

Similar Posts

What Is the Difference Between Factory and Abstract Factory Design Patterns?

When to Use Factory Design Pattern in Java?

Builder Design Pattern

Factory Design Pattern

Adapter Design Pattern

Decorator Design Pattern

FAQ

What is a real example of strategy design pattern?

What is the strategy pattern in game design?

What is difference between strategy and factory design pattern?

What are the benefits of strategy pattern?

90% of Tech Recruiters Judge This In Seconds! 👩‍💻🔍

Don’t let your resume be the weak link. Discover how to make a strong first impression with our free technical resume review!

Related Articles

Types of Memory and Storage in system design thumbnail

Types of Computer Memory and Storage

In this article we will look into different types of computer memory, distinguishing between primary memory and secondary memory. We will also see their characteristics, major kinds, usage, and key

latency in system design thumbnail

What is Latency In System Design?

In this article we will look into Latency In System Design, we will see how is latency introduced into the systems and what are various ways to reduce the latency

Why Aren’t You Getting Interview Calls? 📞❌

It might just be your resume. Let us pinpoint the problem for free and supercharge your job search. 

Newsletter

Don’t miss out! Subscribe now

Log In