← Back to Article List         
Factory Design Pattern in C#

Factory Design Pattern in C#

Published on 26 Sep 2026     17 min read Design Principles & Patterns
Factory Design Pattern

Factory Design Pattern in C#

1. What is the Factory Design Pattern?

The Factory Design Pattern is a Creational Design Pattern used to create objects without directly using new in the client code.

Simple definition

Factory Pattern creates the required object for us based on the input we provide.

Think of a vehicle factory.

You tell the factory:

“I need a Car.”

The factory creates a Car and gives it to you.

You don't need to know how the Car object is created.

Without Factory

IVehicle vehicle = new Car();

The calling code directly knows about Car.

With Factory

IVehicle vehicle = VehicleFactory.CreateVehicle("car");

Now the Factory decides which object should be created.


2. Why do we need Factory Pattern?

Imagine your application supports different vehicle types:

  • Car
  • Bike
  • Bus

Without Factory, your code may contain object creation everywhere:

Car car = new Car();

Bike bike = new Bike();

Bus bus = new Bus();

And sometimes you may write:

if (type == "car")
{
    vehicle = new Car();
}
else if (type == "bike")
{
    vehicle = new Bike();
}
else if (type == "bus")
{
    vehicle = new Bus();
}

If this logic appears in many places, the application becomes difficult to maintain.

Factory Pattern moves the object creation responsibility into one place.

Client
   |
   | "car"
   ↓
VehicleFactory
   |
   | decides what to create
   ↓
  Car

So the client says what it needs, while the factory decides what object to create.


3. Simple Example

We will create three vehicle classes:

             IVehicle
                |
       ---------------------
       |         |         |
      Car       Bike      Bus
       \         |         /
        \        |        /
         VehicleFactory
               |
             Client

4. Full Example Program

Step 1 — Create an Interface

public interface IVehicle
{
    void Drive();
}

IVehicle defines what every vehicle must provide.

In this example, every vehicle should have:

Drive()

Step 2 — Create Car

public class Car : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("Driving a Car");
    }
}

Step 3 — Create Bike

public class Bike : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("Riding a Bike");
    }
}

Step 4 — Create Bus

public class Bus : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("Driving a Bus");
    }
}

All three classes implement the same interface:

IVehicle
   ↑
   |
-------------------
|        |        |
Car     Bike     Bus

5. Create the Factory

This is the important part of the pattern.

public static class VehicleFactory
{
    public static IVehicle CreateVehicle(string vehicleType)
    {
        switch (vehicleType.ToLower())
        {
            case "car":
                return new Car();

            case "bike":
                return new Bike();

            case "bus":
                return new Bus();

            default:
                throw new ArgumentException("Invalid vehicle type");
        }
    }
}

The factory receives the type:

"car"

and returns:

new Car()

Similarly:

Input       Factory creates
----------------------------
"car"   →   Car
"bike"  →   Bike
"bus"   →   Bus

6. Program.cs

Console.WriteLine("Enter vehicle type: car, bike or bus");

string vehicleType = Console.ReadLine()!;

IVehicle vehicle = VehicleFactory.CreateVehicle(vehicleType);

vehicle.Drive();

7. Complete Program

For testing, you can put everything together:

public interface IVehicle
{
    void Drive();
}


public class Car : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("Driving a Car");
    }
}


public class Bike : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("Riding a Bike");
    }
}


public class Bus : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("Driving a Bus");
    }
}


public static class VehicleFactory
{
    public static IVehicle CreateVehicle(string vehicleType)
    {
        switch (vehicleType.ToLower())
        {
            case "car":
                return new Car();

            case "bike":
                return new Bike();

            case "bus":
                return new Bus();

            default:
                throw new ArgumentException("Invalid vehicle type");
        }
    }
}


public class Program
{
    public static void Main()
    {
        Console.WriteLine("Enter vehicle type: car, bike or bus");

        string vehicleType = Console.ReadLine()!;

        IVehicle vehicle =
            VehicleFactory.CreateVehicle(vehicleType);

        vehicle.Drive();
    }
}

Input

car

Output

Driving a Car

If the input is:

bike

Output:

Riding a Bike

8. How does it work?

Suppose the user enters:

car

Step 1 — Program calls Factory

IVehicle vehicle =
    VehicleFactory.CreateVehicle("car");

Notice that the client does not write:

new Car();

Step 2 — Factory checks the type

case "car":
    return new Car();

The Factory creates the object.


Step 3 — Factory returns the object

Even though the actual object is:

Car

we receive it using the interface:

IVehicle vehicle

This is possible because:

Car : IVehicle

Step 4 — Call the method

vehicle.Drive();

Since the actual object is Car, C# executes:

Car.Drive()

Output:

Driving a Car

9. Flow

User enters "car"
       |
       ↓
Program.cs
       |
       ↓
VehicleFactory.CreateVehicle("car")
       |
       ↓
Factory checks vehicle type
       |
       ↓
new Car()
       |
       ↓
Returns IVehicle
       |
       ↓
vehicle.Drive()
       |
       ↓
"Driving a Car"

The important idea is:

Client
  |
  | Request
  ↓
Factory
  |
  | Creates
  ↓
Concrete Object

10. What problem did Factory solve?

Without Factory:

if (vehicleType == "car")
{
    IVehicle vehicle = new Car();
}
else if (vehicleType == "bike")
{
    IVehicle vehicle = new Bike();
}
else if (vehicleType == "bus")
{
    IVehicle vehicle = new Bus();
}

The client is responsible for deciding which class to instantiate.

With Factory:

IVehicle vehicle =
    VehicleFactory.CreateVehicle(vehicleType);

The client doesn't care whether the Factory creates:

new Car()

or:

new Bike()

or:

new Bus()

The creation logic is centralized inside the Factory.


11. Advantages

1. Centralized object creation

Object creation is kept in one place.

VehicleFactory

instead of spreading:

new Car()
new Bike()
new Bus()

throughout the application.

2. Loose coupling

The client mainly works with:

IVehicle

rather than depending directly on every concrete class.

3. Easier maintenance

If object creation becomes complicated, we mainly modify the Factory.

For example:

new Car(engine, logger, configuration);

The client doesn't need to know those creation details.

4. Hides creation complexity

The client simply calls:

VehicleFactory.CreateVehicle("car");

The Factory handles the creation process.

5. Common interface

Different objects can be handled through one interface:

IVehicle

12. Real-world Web API Example

In a Web API application, imagine you support different notification methods:

Email
SMS
WhatsApp

You could have:

INotification notification =
    NotificationFactory.Create("email");

notification.Send();

Factory decides:

"email"     → EmailNotification
"sms"       → SmsNotification
"whatsapp"  → WhatsAppNotification

This is a more realistic use case because the API doesn't need to know the creation details of every notification implementation.


Key Points

  • Factory Pattern is a Creational Design Pattern.
  • Its main purpose is object creation.
  • The client asks the Factory for an object.
  • The Factory decides which concrete class to instantiate.
  • It avoids putting new ConcreteClass() throughout client code.
  • Usually, implementations share a common interface or base class.
  • It helps reduce coupling between client code and concrete implementations.
  • Object creation logic is centralized.
  • It is useful when object type depends on input, configuration, business rules, or runtime conditions.
  • A Factory is especially useful when object creation is complex or may change.
  • In ASP.NET Core, Dependency Injection often handles many object-creation scenarios, but Factory Pattern is still useful when the implementation must be selected dynamically at runtime.

Interview definition

Factory Design Pattern is a creational pattern that centralizes object creation and returns the required implementation based on input or conditions, without making the client directly create the concrete object.

Sure. A slightly more realistic Factory example is a Payment Processing System where the application supports Credit Card, UPI, and Net Banking. It also shows constructor parameters, different implementations, and a factory returning the correct object.

Factory Pattern – Payment Example

Scenario

Imagine an e-commerce application. A customer can choose:

1 → Credit Card
2 → UPI
3 → Net Banking

We don't want the main application to contain:

new CreditCardPayment(...)
new UpiPayment(...)
new NetBankingPayment(...)

Instead:

Client
   ↓
PaymentFactory
   ↓
Chooses implementation
   ↓
IPayment
   ↓
CreditCard / UPI / NetBanking

1. Create the Interface

public interface IPayment
{
    void Pay(decimal amount);
}

Every payment method must implement Pay().


2. Credit Card Payment

public class CreditCardPayment : IPayment
{
    private readonly string _cardNumber;

    public CreditCardPayment(string cardNumber)
    {
        _cardNumber = cardNumber;
    }

    public void Pay(decimal amount)
    {
        Console.WriteLine($"Processing Credit Card payment...");
        Console.WriteLine($"Card: {_cardNumber}");
        Console.WriteLine($"Amount: ₹{amount}");
        Console.WriteLine("Credit Card payment successful.");
    }
}

Here object creation needs additional information:

new CreditCardPayment(cardNumber)

3. UPI Payment

public class UpiPayment : IPayment
{
    private readonly string _upiId;

    public UpiPayment(string upiId)
    {
        _upiId = upiId;
    }

    public void Pay(decimal amount)
    {
        Console.WriteLine("Processing UPI payment...");
        Console.WriteLine($"UPI ID: {_upiId}");
        Console.WriteLine($"Amount: ₹{amount}");
        Console.WriteLine("UPI payment successful.");
    }
}

4. Net Banking Payment

public class NetBankingPayment : IPayment
{
    private readonly string _bankName;

    public NetBankingPayment(string bankName)
    {
        _bankName = bankName;
    }

    public void Pay(decimal amount)
    {
        Console.WriteLine("Processing Net Banking payment...");
        Console.WriteLine($"Bank: {_bankName}");
        Console.WriteLine($"Amount: ₹{amount}");
        Console.WriteLine("Net Banking payment successful.");
    }
}

5. Create Payment Request

Instead of passing many individual parameters to the Factory, we can create a request class.

public class PaymentRequest
{
    public string PaymentType { get; set; } = string.Empty;

    public string CardNumber { get; set; } = string.Empty;

    public string UpiId { get; set; } = string.Empty;

    public string BankName { get; set; } = string.Empty;
}

6. Create the Factory

This is the main part of the pattern.

public static class PaymentFactory
{
    public static IPayment CreatePayment(PaymentRequest request)
    {
        switch (request.PaymentType.ToLower())
        {
            case "creditcard":
                return new CreditCardPayment(
                    request.CardNumber);

            case "upi":
                return new UpiPayment(
                    request.UpiId);

            case "netbanking":
                return new NetBankingPayment(
                    request.BankName);

            default:
                throw new ArgumentException(
                    "Invalid payment type");
        }
    }
}

Notice that the new statements are now centralized inside the Factory.


7. Program.cs

Console.WriteLine("Select Payment Type:");
Console.WriteLine("1. Credit Card");
Console.WriteLine("2. UPI");
Console.WriteLine("3. Net Banking");

string? choice = Console.ReadLine();

PaymentRequest request = new PaymentRequest();

switch (choice)
{
    case "1":

        request.PaymentType = "creditcard";

        Console.Write("Enter Card Number: ");
        request.CardNumber = Console.ReadLine()!;

        break;

    case "2":

        request.PaymentType = "upi";

        Console.Write("Enter UPI ID: ");
        request.UpiId = Console.ReadLine()!;

        break;

    case "3":

        request.PaymentType = "netbanking";

        Console.Write("Enter Bank Name: ");
        request.BankName = Console.ReadLine()!;

        break;

    default:

        Console.WriteLine("Invalid option");
        return;
}

Console.Write("Enter Amount: ");

decimal amount = Convert.ToDecimal(
    Console.ReadLine());


// Factory creates the correct object
IPayment payment =
    PaymentFactory.CreatePayment(request);


// Client doesn't care about actual class
payment.Pay(amount);

How It Works

Suppose the customer selects:

2. UPI

and enters:

UPI ID : syed@upi
Amount : 5000

The application prepares:

request.PaymentType = "upi";
request.UpiId = "syed@upi";

Then:

IPayment payment =
    PaymentFactory.CreatePayment(request);

The Factory checks:

case "upi":
    return new UpiPayment(request.UpiId);

So internally:

payment
   ↓
IPayment reference
   ↓
Actual Object = UpiPayment

Then the client simply calls:

payment.Pay(5000);

Output:

Processing UPI payment...
UPI ID: syed@upi
Amount: ₹5000
UPI payment successful.

Why Factory Helps Here

Without Factory, the calling code needs to know how every payment object is created:

if (type == "creditcard")
{
    payment = new CreditCardPayment(cardNumber);
}
else if (type == "upi")
{
    payment = new UpiPayment(upiId);
}
else if (type == "netbanking")
{
    payment = new NetBankingPayment(bankName);
}

With Factory:

IPayment payment =
    PaymentFactory.CreatePayment(request);

payment.Pay(amount);

The client says:

Give me the appropriate payment processor.

The Factory decides:

Which concrete payment class should I create?


Complete Flow

Customer
   │
   │ Selects "UPI"
   ↓
Program.cs
   │
   │ PaymentRequest
   ↓
PaymentFactory
   │
   │ checks PaymentType
   ↓
new UpiPayment("syed@upi")
   │
   ↓
IPayment
   │
   │ Pay(5000)
   ↓
UpiPayment.Pay()
   │
   ↓
Payment Successful

Important Point

The biggest benefit is not simply avoiding the new keyword. Somewhere in the application, an object still has to be created.

The important point is:

The client does not need to know which concrete class to create or how to create it. That responsibility is moved to the Factory.

Key Points

  • IPayment provides a common contract.
  • CreditCardPayment, UpiPayment, and NetBankingPayment are concrete implementations.
  • PaymentFactory contains the object-selection and creation logic.
  • The client works mainly with IPayment.
  • Different constructors can be handled by the Factory.
  • Runtime input can determine which implementation is created.
  • Creation logic is kept in one location.
  • This reduces direct dependency between the client and concrete payment classes.
  • In a production ASP.NET Core application, we would normally combine this idea with Dependency Injection rather than manually constructing all dependencies inside a static Factory.

Factory Pattern — More Complex C# Example

Let's use a realistic Notification System. This is more complex because each notification type has its own service dependency, and the factory decides which implementation to create.

Scenario

An application sends notifications through:

  • Email
  • SMS
  • WhatsApp

The calling code should simply say:

INotification notification =
    factory.CreateNotification("email");

notification.Send("Welcome to our application");

It should not worry about how EmailNotification is constructed.


1. Overall Structure

                    INotification
                         │
          ┌──────────────┼──────────────┐
          │              │              │
 EmailNotification  SmsNotification  WhatsAppNotification
          │              │              │
      EmailService    SmsService    WhatsAppService
          ▲              ▲              ▲
          └──────────────┼──────────────┘
                         │
                NotificationFactory
                         │
                         ▲
                     Program

2. Notification Interface

public interface INotification
{
    void Send(string message);
}

Every notification must provide a Send() method.


3. Supporting Services

In a real application, notification classes may depend on external services.

Email Service

public class EmailService
{
    public void SendEmail(string email, string message)
    {
        Console.WriteLine($"Email sent to: {email}");
        Console.WriteLine($"Message: {message}");
    }
}

SMS Service

public class SmsService
{
    public void SendSms(string mobile, string message)
    {
        Console.WriteLine($"SMS sent to: {mobile}");
        Console.WriteLine($"Message: {message}");
    }
}

WhatsApp Service

public class WhatsAppService
{
    public void SendWhatsApp(string mobile, string message)
    {
        Console.WriteLine($"WhatsApp sent to: {mobile}");
        Console.WriteLine($"Message: {message}");
    }
}

4. Email Notification

public class EmailNotification : INotification
{
    private readonly EmailService _emailService;
    private readonly string _email;

    public EmailNotification(
        EmailService emailService,
        string email)
    {
        _emailService = emailService;
        _email = email;
    }

    public void Send(string message)
    {
        _emailService.SendEmail(_email, message);
    }
}

Notice that EmailNotification itself doesn't directly perform the email operation.

It uses:

EmailService

This is Dependency Injection through the constructor.


5. SMS Notification

public class SmsNotification : INotification
{
    private readonly SmsService _smsService;
    private readonly string _mobile;

    public SmsNotification(
        SmsService smsService,
        string mobile)
    {
        _smsService = smsService;
        _mobile = mobile;
    }

    public void Send(string message)
    {
        _smsService.SendSms(_mobile, message);
    }
}

6. WhatsApp Notification

public class WhatsAppNotification : INotification
{
    private readonly WhatsAppService _whatsAppService;
    private readonly string _mobile;

    public WhatsAppNotification(
        WhatsAppService whatsAppService,
        string mobile)
    {
        _whatsAppService = whatsAppService;
        _mobile = mobile;
    }

    public void Send(string message)
    {
        _whatsAppService.SendWhatsApp(
            _mobile,
            message);
    }
}

7. Notification Request

Now we create a request containing the information required by the Factory.

public class NotificationRequest
{
    public string Type { get; set; } = string.Empty;

    public string Email { get; set; } = string.Empty;

    public string Mobile { get; set; } = string.Empty;

    public string Message { get; set; } = string.Empty;
}

For example:

var request = new NotificationRequest
{
    Type = "email",
    Email = "syed@example.com",
    Message = "Your order has been placed."
};

8. Create Factory Interface

Instead of directly creating a static Factory, let's also create an interface.

public interface INotificationFactory
{
    INotification CreateNotification(
        NotificationRequest request);
}

Why?

Because later the Factory itself can be registered with ASP.NET Core Dependency Injection.


9. Create Notification Factory

Now comes the important part.

public class NotificationFactory : INotificationFactory
{
    private readonly EmailService _emailService;
    private readonly SmsService _smsService;
    private readonly WhatsAppService _whatsAppService;

    public NotificationFactory(
        EmailService emailService,
        SmsService smsService,
        WhatsAppService whatsAppService)
    {
        _emailService = emailService;
        _smsService = smsService;
        _whatsAppService = whatsAppService;
    }

    public INotification CreateNotification(
        NotificationRequest request)
    {
        switch (request.Type.ToLower())
        {
            case "email":

                return new EmailNotification(
                    _emailService,
                    request.Email);

            case "sms":

                return new SmsNotification(
                    _smsService,
                    request.Mobile);

            case "whatsapp":

                return new WhatsAppNotification(
                    _whatsAppService,
                    request.Mobile);

            default:

                throw new ArgumentException(
                    $"Unsupported notification type: {request.Type}");
        }
    }
}

Now our Factory itself has dependencies:

NotificationFactory
       │
       ├── EmailService
       ├── SmsService
       └── WhatsAppService

This is closer to how factories appear in real applications.


10. Register Services

If this were an ASP.NET Core application, we could register them in Program.cs:

builder.Services.AddScoped<EmailService>();

builder.Services.AddScoped<SmsService>();

builder.Services.AddScoped<WhatsAppService>();

builder.Services.AddScoped
    <INotificationFactory, NotificationFactory>();

ASP.NET Core's DI container will create:

NotificationFactory
        ↓
Inject EmailService
Inject SmsService
Inject WhatsAppService

11. Use Factory from Web API Controller

Now consider an API:

POST /api/notification

Controller:

[ApiController]
[Route("api/[controller]")]
public class NotificationController : ControllerBase
{
    private readonly INotificationFactory _factory;

    public NotificationController(
        INotificationFactory factory)
    {
        _factory = factory;
    }

    [HttpPost]
    public IActionResult Send(
        NotificationRequest request)
    {
        INotification notification =
            _factory.CreateNotification(request);

        notification.Send(request.Message);

        return Ok("Notification sent successfully");
    }
}

This is where the Factory Pattern becomes much more useful.

The controller doesn't contain:

new EmailNotification(...)

or:

new SmsNotification(...)

or:

new WhatsAppNotification(...)

It only knows:

INotification

and:

INotificationFactory

12. Example Request — Email

The client sends:

{
    "type": "email",
    "email": "syed@example.com",
    "message": "Your order has been placed successfully."
}

Controller receives the request:

_factory.CreateNotification(request);

Factory sees:

Type = "email"

So it creates:

new EmailNotification(
    _emailService,
    request.Email);

Then the controller calls:

notification.Send(request.Message);

Output:

Email sent to: syed@example.com
Message: Your order has been placed successfully.

13. Example Request — SMS

Request:

{
    "type": "sms",
    "mobile": "9876543210",
    "message": "Your OTP is 123456."
}

Factory sees:

sms

and returns:

new SmsNotification(
    _smsService,
    request.Mobile);

Then:

notification.Send(request.Message);

Output:

SMS sent to: 9876543210
Message: Your OTP is 123456.

14. Complete Flow

For an email request:

POST /api/notification
        │
        │
        ▼
NotificationController
        │
        │ request.Type = "email"
        ▼
INotificationFactory
        │
        ▼
NotificationFactory
        │
        │ Select Email
        ▼
new EmailNotification(...)
        │
        │ Inject EmailService
        ▼
EmailNotification
        │
        │ Send()
        ▼
EmailService
        │
        ▼
Email Sent

The important separation is:

Controller
    │
    │ "Give me the correct notification"
    ▼
Factory
    │
    │ decides which class
    ▼
Concrete Notification
    │
    │ uses appropriate service
    ▼
External System

15. Why Not Put switch in Controller?

Technically, we could do this:

[HttpPost]
public IActionResult Send(NotificationRequest request)
{
    if (request.Type == "email")
    {
        var notification =
            new EmailNotification(...);
    }
    else if (request.Type == "sms")
    {
        var notification =
            new SmsNotification(...);
    }

    // ...
}

But now the controller has two responsibilities:

1. Handle HTTP request
2. Decide how notification objects are created

That's not desirable.

With Factory:

Controller
   ↓
Handles HTTP request

Factory
   ↓
Handles object selection/creation

Notification
   ↓
Handles notification operation

This gives better separation of concerns.


16. What Happens When We Add Push Notification?

Suppose later the requirement changes:

"We also need mobile Push Notifications."

Create:

public class PushNotification : INotification
{
    private readonly string _deviceId;

    public PushNotification(string deviceId)
    {
        _deviceId = deviceId;
    }

    public void Send(string message)
    {
        Console.WriteLine(
            $"Push notification sent to {_deviceId}");

        Console.WriteLine(message);
    }
}

Then add the relevant creation logic to the Factory.

The controller remains:

INotification notification =
    _factory.CreateNotification(request);

notification.Send(request.Message);

No change is required to the controller's basic workflow.


Factory + Dependency Injection

There are actually two different responsibilities here.

Dependency Injection

DI creates and supplies services such as:

EmailService
SmsService
WhatsAppService
NotificationFactory

Factory

Factory makes a runtime decision:

What notification does this request need?

email?
sms?
whatsapp?

So they can work together:

ASP.NET Core DI
       │
       ▼
NotificationFactory
       │
       │ Runtime decision
       ▼
Which implementation?
       │
  ┌────┼────────┐
  ↓    ↓        ↓
Email SMS   WhatsApp

This is a very common reason to introduce a Factory: the correct implementation cannot be known until runtime data arrives.


One Important Design Improvement

The example deliberately uses:

switch (request.Type.ToLower())

because it makes the Factory concept easy to understand.

But as the number of implementations grows:

Email
SMS
WhatsApp
Push
Teams
Slack
Telegram
...

a large switch starts becoming difficult to maintain.

In a more advanced implementation, we can register multiple implementations with ASP.NET Core DI and let the Factory select an already-registered implementation, instead of manually doing:

new EmailNotification(...)

That gives a cleaner architecture:

Controller
    ↓
Factory
    ↓
DI Container
    ↓
Select correct INotification
    ↓
Email / SMS / WhatsApp

Key Points

  • Factory is a Creational Design Pattern.
  • It centralizes object selection/creation.
  • Client code depends mainly on interfaces, not concrete classes.
  • Factory is especially useful when the implementation is selected at runtime.
  • Different implementations can have different dependencies.
  • Factory and Dependency Injection can work together.
  • DI manages dependencies and object lifetimes.
  • Factory handles the runtime decision of which implementation to use.
  • It keeps object-selection logic out of controllers and business services.
  • For a small number of implementations, a switch Factory can be sufficient.
  • For a larger production system, DI-based Factory / strategy resolution is usually cleaner than continuously expanding a switch.

Interview-level understanding: Factory is not simply about hiding the new keyword. Its real value is separating the decision and construction of concrete implementations from the code that uses those implementations.