Hook
The Catchy Title Ideas
Clean Architecture in C#: Say Goodbye to Endless if/else with Strategy Pattern
Refactoring C#: Implementing the Strategy Pattern for Scalable Code
The Hook
Describe a scenario every developer faces—a simple feature that grew into an unmaintainable, 500-line switch statement.
Objective: Introduce the Strategy Pattern as a elegant solution to make business logic modular, testable, and compliant with SOLID principles.
The Problem: The "Switch Statement" Anti-Pattern
Scenario
Processing payments (Credit Card, PayPal, Crypto) in an e-commerce checkout.
Code Example (The Bad Way)
Show a brittle switch (paymentType) block.
Why It Fails
Violates the Open/Closed Principle (OCP): Adding a new payment method requires modifying core checkout logic.
Hard to Unit Test: Mocking external APIs inside monolithic classes is painful.
Violates Single Responsibility Principle (SRP): The checkout service knows too much about individual provider APIs.
What is the Strategy Pattern?
Plain English Definition
A behavioral pattern that defines a family of algorithms, puts each in its own class, and makes them interchangeable at runtime.
Analogy
Choosing a mode of transport to the airport (Car, Train, Bus). The goal is the same, but the mechanism varies depending on conditions.
Real-World Scenario: Dynamic Payment Gateway
A classic real-world scenario is an E-Commerce Checkout System handling multiple payment methods. Using if/else or switch statements to handle dynamic payment logic violates the Open/Closed Principle (solid principles), as adding a new payment method requires modifying core checkout logic.
Here is a clean, production-ready implementation in C#.
1. Strategy Interface
Defines the contract all payment algorithms must implement.
public interface IPaymentStrategy
{
Task<PaymentResult> ProcessPaymentAsync(decimal amount, string currency);
}
public record PaymentResult(bool IsSuccess, string TransactionId, string ErrorMessage = "");
2. Concrete Strategies
Each concrete strategy encapsulates the specific API calls and validation rules for a given provider.
// Credit Card Strategy
public class CreditCardPaymentStrategy : IPaymentStrategy
{
private readonly string _cardNumber;
private readonly string _cvv;
public CreditCardPaymentStrategy(string cardNumber, string cvv)
{
_cardNumber = cardNumber;
_cvv = cvv;
}
public async Task<PaymentResult> ProcessPaymentAsync(decimal amount, string currency)
{
// Simulate credit card gateway API integration (e.g., Stripe)
await Task.Delay(100); // Network call
Console.WriteLine($"[CreditCard] Processed {currency} {amount} via card Ending in {_cardNumber[^4..]}");
return new PaymentResult(true, $"CC-TXN-{Guid.NewGuid().ToString()[..8]}");
}
}
// PayPal Strategy
public class PayPalPaymentStrategy : IPaymentStrategy
{
private readonly string _email;
public PayPalPaymentStrategy(string email)
{
_email = email;
}
public async Task<PaymentResult> ProcessPaymentAsync(decimal amount, string currency)
{
// Simulate PayPal OAuth & charge API
await Task.Delay(100);
Console.WriteLine($"[PayPal] Processed {currency} {amount} for account {_email}");
return new PaymentResult(true, $"PP-TXN-{Guid.NewGuid().ToString()[..8]}");
}
}
// Crypto Strategy
public class CryptoPaymentStrategy : IPaymentStrategy
{
private readonly string _walletAddress;
public CryptoPaymentStrategy(string walletAddress)
{
_walletAddress = walletAddress;
}
public async Task<PaymentResult> ProcessPaymentAsync(decimal amount, string currency)
{
// Simulate blockchain transaction submission
await Task.Delay(100);
Console.WriteLine($"[Crypto] Processed {currency} {amount} to wallet {_walletAddress[..6]}...");
return new PaymentResult(true, $"CRYPTO-TXN-{Guid.NewGuid().ToString()[..8]}");
}
}

Join the conversation! Your thoughts help the community grow.