Skip to main content

Command Palette

Search for a command to run...

How Factory and Strategy Patterns Work Together

Understanding the Synergy Between Factory and Strategy Design Patterns in C#

Published
4 min read

In software development, writing flexible and maintainable code is key. Two of the most useful design patterns for achieving this are the Factory and Strategy patterns. While they address different challenges, combining them can make your system highly extensible and robust.

In this post, we’ll explore how to implement a payment system with dynamic behaviors like discounts to see how these patterns work together in a real-world scenario.

What is the Factory Pattern?

The Factory Pattern is a creational design pattern. Its purpose is to decouple the client from the creation of objects.

Imagine a payment system where a user can choose Credit Card, PayPal, or bKash.

  • The client shouldn’t worry about which class is instantiated.

  • The Factory decides which payment object to create.

This separation makes the system easy to extend with new payment methods without changing the client code.

What is the Strategy Pattern?

The Strategy Pattern is a behavioral design pattern. It allows changing the behavior of an object dynamically at runtime.

In our payment system, different customers may have different discount rules:

  • Regular customer → no discount

  • Premium customer → 10% discount

  • Festival offer → 20% discount

  • Corporate deal → custom discount

With the Strategy Pattern, these rules can change without modifying the payment class.

How Factory and Strategy Work Together

By combining these patterns:

  1. Factory creates the appropriate payment type (Credit Card, PayPal, bKash).

  2. Strategy applies the correct discount dynamically based on customer type or promotion.

This combination allows you to:

  • Add new payment methods easily

  • Introduce new discount strategies without touching existing code

Sample Code Implementation in C

Step 1: Define Discount Strategies

public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal amount);
}

public class RegularCustomerDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal amount) => amount;
}

public class PremiumCustomerDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal amount) => amount * 0.90m;
}

public class FestivalDiscount : IDiscountStrategy
{
    public decimal ApplyDiscount(decimal amount) => amount * 0.80m;
}

public class CorporateDealDiscount : IDiscountStrategy
{
    private readonly decimal _discountPercentage;

    public CorporateDealDiscount(decimal discountPercentage)
    {
        _discountPercentage = discountPercentage;
    }

    public decimal ApplyDiscount(decimal amount) =>
        amount - (amount * _discountPercentage / 100);
}

Step 2: Define Payment Interface and Classes

public interface IPayment
{
    void SetDiscountStrategy(IDiscountStrategy discountStrategy);
    void ProcessPayment(decimal amount);
}

public class CreditCardPayment : IPayment
{
    private IDiscountStrategy _discountStrategy = new RegularCustomerDiscount();

    public void SetDiscountStrategy(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }

    public void ProcessPayment(decimal amount)
    {
        var finalAmount = _discountStrategy.ApplyDiscount(amount);
        Console.WriteLine($"Paid {finalAmount:C} using Credit Card.");
    }
}

public class PayPalPayment : IPayment
{
    private IDiscountStrategy _discountStrategy = new RegularCustomerDiscount();

    public void SetDiscountStrategy(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }

    public void ProcessPayment(decimal amount)
    {
        var finalAmount = _discountStrategy.ApplyDiscount(amount);
        Console.WriteLine($"Paid {finalAmount:C} using PayPal.");
    }
}

public class BkashPayment : IPayment
{
    private IDiscountStrategy _discountStrategy = new RegularCustomerDiscount();

    public void SetDiscountStrategy(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }

    public void ProcessPayment(decimal amount)
    {
        var finalAmount = _discountStrategy.ApplyDiscount(amount);
        Console.WriteLine($"Paid {finalAmount:C} using bKash.");
    }
}

Step 3: Create the Payment Factory

public static class PaymentFactory
{
    public static IPayment CreatePayment(string paymentMethod)
    {
        return paymentMethod.ToLower() switch
        {
            "creditcard" => new CreditCardPayment(),
            "paypal" => new PayPalPayment(),
            "bkash" => new BkashPayment(),
            _ => throw new NotSupportedException("Payment method not supported")
        };
    }
}

Step 4: Example Usage

        // Regular customer paying with Credit Card
        var creditCardPayment = PaymentFactory.CreatePayment("creditcard");
        creditCardPayment.SetDiscountStrategy(new RegularCustomerDiscount());
        creditCardPayment.ProcessPayment(100m);

        // Premium customer paying with PayPal
        var payPalPayment = PaymentFactory.CreatePayment("paypal");
        payPalPayment.SetDiscountStrategy(new PremiumCustomerDiscount());
        payPalPayment.ProcessPayment(100m);

        // Corporate deal paying with Credit Card (custom 25%)
        var corporatePayment = PaymentFactory.CreatePayment("creditcard");
        corporatePayment.SetDiscountStrategy(new CorporateDealDiscount(25));
        corporatePayment.ProcessPayment(100m);

Benefits of This Approach

  • Extensibility: Easily add new payment methods or discount strategies.

  • Maintainability: Each class has a single responsibility.

  • Flexibility: Change discount rules at runtime without touching payment logic.

By combining Factory and Strategy, your payment system becomes dynamic, clean, and future-proof.

Don’t Confuse Factory and Strategy

It’s easy to mix up Factory and Strategy because both involve objects, but they solve completely different problems.

  • The Factory Pattern is all about creating objects. It decides which class should be instantiated so the client doesn’t have to know. For example, in a payment system, the Factory chooses between CreditCardPayment, PayPalPayment, or BkashPayment. The client just asks for a payment method, and the Factory delivers the correct object.

  • The Strategy Pattern, on the other hand, is about changing behavior at runtime. Once you have a payment object, you might want to apply different discount rules—regular, premium, festival, or corporate. Strategy lets you swap the discount logic without modifying the payment class itself.

Think of it this way:

  • Factory = which tool you pick

  • Strategy = how you use the tool