Introduction

In the world of software development, the ability to adapt and switch algorithms dynamically is crucial. The Strategy Pattern, one of the fundamental design patterns, provides an elegant solution to achieve this flexibility. In this article, we will explore how to implement the Strategy Pattern in .NET 8 with practical coding examples.

Understanding the Strategy Pattern

The Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each algorithm, and makes them interchangeable. This allows the client to choose the appropriate algorithm at runtime, without modifying the client’s code. The pattern involves creating a family of algorithms, encapsulating each one, and making them interchangeable.

Setting Up a .NET 8 Project

Let’s start by setting up a new .NET 8 project. Open a terminal and run the following commands:

bash
dotnet new console -n StrategyPatternExample
cd StrategyPatternExample

This will create a new console application.

Creating the Strategy Interface

Define the IStrategy interface that declares the method(s) common to all supported algorithms:

csharp
public interface IStrategy
{
void Execute();
}

Implementing Concrete Strategies

Create concrete strategy classes that implement the IStrategy interface. For example, let’s create two strategies: ConcreteStrategyA and ConcreteStrategyB:

csharp
public class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine("Executing Concrete Strategy A");
}
}
public class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine(“Executing Concrete Strategy B”);
}
}

Creating the Context

The context class is responsible for holding a reference to the chosen strategy and providing methods to change it. Define the Context class:

csharp
public class Context
{
private IStrategy _strategy;
public Context(IStrategy strategy)
{
_strategy = strategy;
}public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}public void ExecuteStrategy()
{
_strategy.Execute();
}
}

Putting It All Together

Now, let’s use these components in the Main method of the console application:

csharp
class Program
{
static void Main(string[] args)
{
// Create strategies
IStrategy strategyA = new ConcreteStrategyA();
IStrategy strategyB = new ConcreteStrategyB();
// Create context with default strategy
Context context = new Context(strategyA);// Execute default strategy
context.ExecuteStrategy();// Change strategy at runtime
context.SetStrategy(strategyB);

// Execute new strategy
context.ExecuteStrategy();
}
}

Benefits of the Strategy Pattern

1. Flexibility and Extensibility

The Strategy Pattern promotes a flexible and extensible codebase. Adding new algorithms is straightforward, and existing code remains untouched.

2. Separation of Concerns

The pattern separates the algorithm’s implementation from the client code, enhancing code maintainability. Changes in one algorithm don’t affect others.

3. Easier Testing

Each strategy can be tested independently, simplifying the testing process and ensuring the correctness of each algorithm.

Real-world Use Case: Payment Processing

Let’s consider a real-world scenario where the Strategy Pattern can be applied: payment processing in an e-commerce application.

Payment Strategies

Define payment strategy interfaces and concrete implementations:

csharp
public interface IPaymentStrategy
{
void ProcessPayment(double amount);
}
public class CreditCardPaymentStrategy : IPaymentStrategy
{
public void ProcessPayment(double amount)
{
Console.WriteLine($”Processing credit card payment of ${amount});
// Additional credit card processing logic
}
}public class PayPalPaymentStrategy : IPaymentStrategy
{
public void ProcessPayment(double amount)
{
Console.WriteLine($”Processing PayPal payment of ${amount});
// Additional PayPal processing logic
}
}

PaymentContext

Create a PaymentContext class to encapsulate the payment strategy:

csharp
public class PaymentContext
{
private IPaymentStrategy _paymentStrategy;
public PaymentContext(IPaymentStrategy paymentStrategy)
{
_paymentStrategy = paymentStrategy;
}public void SetPaymentStrategy(IPaymentStrategy paymentStrategy)
{
_paymentStrategy = paymentStrategy;
}public void ProcessPayment(double amount)
{
_paymentStrategy.ProcessPayment(amount);
}
}

Implementing Payment Processing

In the application, create instances of payment strategies and use the PaymentContext to process payments:

csharp
class Program
{
static void Main(string[] args)
{
// Create payment strategies
IPaymentStrategy creditCardStrategy = new CreditCardPaymentStrategy();
IPaymentStrategy paypalStrategy = new PayPalPaymentStrategy();
// Create payment context with default strategy
PaymentContext paymentContext = new PaymentContext(creditCardStrategy);// Process payment using default strategy
paymentContext.ProcessPayment(100.0);// Switch to PayPal strategy
paymentContext.SetPaymentStrategy(paypalStrategy);

// Process payment using PayPal strategy
paymentContext.ProcessPayment(50.0);
}
}

Conclusion

The Strategy Pattern is a powerful tool in a developer’s arsenal, providing an elegant solution to scenarios where different algorithms or behaviors need to be dynamically interchangeable. In this article, we walked through the implementation of the Strategy Pattern in .NET 8, from setting up the environment to creating the necessary interfaces and classes.

By leveraging the Strategy Pattern, developers can write more flexible and scalable code, making it easier to adapt to changing requirements. As you continue to explore design patterns, consider incorporating the Strategy Pattern when faced with scenarios that involve dynamic behavior selection.