DEV Community

Binoy Vijayan
Binoy Vijayan

Posted on • Edited on

GoF- Strategy Pattern

A behavioural design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. This allows the client to choose the appropriate algorithm at runtime without altering the client's code.

Structure:

Context: Maintains a reference to a strategy object and may define an interface that lets the strategy access its data.

Strategy: Declares an interface common to all supported algorithms.

Concrete Strategies: Implement the algorithm defined by the strategy interface.

Explanation:

The Context class is the class that contains the business logic where the strategies are applied. It contains a reference to a Strategy object and can switch between different strategies at runtime.

The Strategy interface declares the method(s) that all concrete strategies must implement. It allows the Context to interact with the strategies using a common interface.

The Concrete Strategies are the actual implementations of the algorithms defined by the Strategy interface. Each concrete strategy encapsulates a specific algorithm.

Clients interact with the Context object, not directly with the concrete strategies. The client can configure the Context with different strategies based on its requirements.

Example:

Let's consider a payment processing system where different payment methods (strategies) such as Credit Card, PayPal, and Bitcoin are available. We can use the Strategy pattern to encapsulate each payment method as a separate strategy:

// Strategy protocol
protocol PaymentStrategy {
    func pay(amount: Double)
}

// Concrete Strategies
class CreditCardStrategy: PaymentStrategy {
    func pay(amount: Double) {
        print("Paying \(amount) via Credit Card")
        // Implementation for credit card payment
    }
}

class PayPalStrategy: PaymentStrategy {
    func pay(amount: Double) {
        print("Paying \(amount) via PayPal")
        // Implementation for PayPal payment
    }
}

class BitcoinStrategy: PaymentStrategy {
    func pay(amount: Double) {
        print("Paying \(amount) via Bitcoin")
        // Implementation for Bitcoin payment
    }
}

// Context
class PaymentContext {
    private var strategy: PaymentStrategy

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

    func setStrategy(strategy: PaymentStrategy) {
        self.strategy = strategy
    }

    func executePayment(amount: Double) {
        strategy.pay(amount: amount)
    }
}

// Client code
let paymentContext = PaymentContext(strategy: CreditCardStrategy())
paymentContext.executePayment(amount: 100.0) // Output: Paying 100.0 via Credit Card

paymentContext.setStrategy(strategy: PayPalStrategy())
paymentContext.executePayment(amount: 50.0) // Output: Paying 50.0 via PayPal

paymentContext.setStrategy(strategy: BitcoinStrategy())
paymentContext.executePayment(amount: 200.0) // Output: Paying 200.0 via Bitcoin
Enter fullscreen mode Exit fullscreen mode

In this example:

We have a PaymentStrategy protocol defining the interface for all payment strategies.

Concrete strategies like CreditCardStrategy, PayPalStrategy, and BitcoinStrategy implement the PaymentStrategy protocol.

The PaymentContext class is the context where payment strategies are applied. It holds a reference to the current payment strategy and delegates payment execution to it.

Clients interact with the PaymentContext object, setting different payment strategies as needed.

This structure allows for easy addition of new payment strategies without modifying existing code. It also promotes flexibility by allowing the client to switch between payment strategies dynamically.

Image description

Usage

Here are some common scenarios where the Strategy pattern can be applied effectively:

Sorting Algorithms: In sorting algorithms, such as bubble sort, merge sort, or quick sort, the Strategy pattern can be used to encapsulate each sorting algorithm into its own class. The client can then choose the appropriate sorting strategy based on factors like input size, data distribution, or memory constraints.

Compression and Encryption: In compression and encryption algorithms, such as gzip compression or AES encryption, the Strategy pattern can be used to encapsulate different compression or encryption algorithms. The client can select the desired strategy based on factors like performance, security requirements, or compatibility with other systems.

Payment Processing: In payment processing systems, such as e-commerce platforms, the Strategy pattern can be used to represent different payment methods (e.g., credit card, PayPal, Bitcoin). Each payment method is encapsulated into its own strategy class, allowing the client to select the desired payment method at checkout.

Routing and Navigation: In routing and navigation systems, such as GPS navigation applications, the Strategy pattern can be used to represent different routing algorithms (e.g., shortest path, fastest route, avoid toll roads). Each routing algorithm is encapsulated into its own strategy class, allowing the user to choose the preferred route based on factors like time, distance, or traffic conditions.

Text Processing: In text processing applications, such as word processors or text editors, the Strategy pattern can be used to represent different text formatting or parsing algorithms (e.g., plain text, HTML, Markdown). Each text processing algorithm is encapsulated into its own strategy class, allowing the user to choose the desired format or syntax.

Data Validation: In data validation systems, such as form validation in web applications, the Strategy pattern can be used to represent different validation rules (e.g., required fields, email validation, numeric range validation). Each validation rule is encapsulated into its own strategy class, allowing the client to apply custom validation logic based on input data.

Summary

Overall, the Strategy pattern is beneficial in scenarios where different algorithms or behaviours need to be applied dynamically or where the client code needs to be decoupled from the implementation details of the algorithms. It promotes flexibility, maintainability, and code reuse by encapsulating algorithms into interchangeable strategy classes.

Overview of GoF Design Patterns

Top comments (0)