The Factory Pattern in C#: Creating Objects the Smart Way

Daniel Azevedo - Sep 2 - - Dev Community

Hey devs!

Today, I want to dive into one of the most practical design patterns in C#: the Factory Pattern. If you’ve ever found yourself writing repetitive code to create objects, or struggling with code that’s hard to maintain because of too many new statements scattered around, the Factory Pattern might be just what you need.

What is the Factory Pattern?

The Factory Pattern is a creational design pattern that focuses on the process of creating objects. Instead of instantiating objects directly using new, you delegate that responsibility to a factory. The factory decides which concrete class to instantiate, making your code more flexible and easier to maintain.

Why Should You Care About the Factory Pattern?

Here’s why the Factory Pattern is worth considering:

  1. Simplifies Object Creation: It abstracts away the logic of object creation, so you don’t have to worry about the details every time you need a new instance.
  2. Promotes Loose Coupling: By depending on interfaces or abstract classes rather than concrete implementations, your code becomes less dependent on specific classes, making it easier to change or extend later.
  3. Centralizes Object Creation: Instead of having new statements all over your codebase, the Factory Pattern centralizes object creation in one place, which is great for managing complexity.

A Simple Example in C#

Let’s say you’re building a notification system where you send different types of notifications (e.g., Email, SMS, Push). Without the Factory Pattern, you might end up with something like this:

public void SendNotification(string type)
{
    if (type == "Email")
    {
        var notification = new EmailNotification();
        notification.Send();
    }
    else if (type == "SMS")
    {
        var notification = new SmsNotification();
        notification.Send();
    }
    else if (type == "Push")
    {
        var notification = new PushNotification();
        notification.Send();
    }
}
Enter fullscreen mode Exit fullscreen mode

This works, but it’s not scalable. As you add more notification types, this method will get bloated and harder to maintain. Here’s how the Factory Pattern can clean things up:

// Step 1: Create an interface for your product
public interface INotification
{
    void Send();
}

// Step 2: Implement concrete products
public class EmailNotification : INotification
{
    public void Send()
    {
        Console.WriteLine("Sending Email Notification");
    }
}

public class SmsNotification : INotification
{
    public void Send()
    {
        Console.WriteLine("Sending SMS Notification");
    }
}

public class PushNotification : INotification
{
    public void Send()
    {
        Console.WriteLine("Sending Push Notification");
    }
}

// Step 3: Create the Factory
public class NotificationFactory
{
    public INotification CreateNotification(string type)
    {
        return type switch
        {
            "Email" => new EmailNotification(),
            "SMS" => new SmsNotification(),
            "Push" => new PushNotification(),
            _ => throw new ArgumentException("Invalid notification type")
        };
    }
}

// Step 4: Use the Factory in your code
public class NotificationService
{
    private readonly NotificationFactory _factory;

    public NotificationService()
    {
        _factory = new NotificationFactory();
    }

    public void SendNotification(string type)
    {
        var notification = _factory.CreateNotification(type);
        notification.Send();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Cleaner Code: The logic for creating notifications is centralized in the NotificationFactory, making the SendNotification method in the NotificationService much simpler and easier to manage.
  • Scalability: Adding a new notification type is straightforward—just create a new class that implements INotification and update the factory. No need to touch the existing code.
  • Flexibility: If you need to change how notifications are created (e.g., adding some configuration), you only need to modify the factory.

When to Use the Factory Pattern

The Factory Pattern is a great choice when:

  • You have a complex creation logic that you want to encapsulate.
  • You need to return different subclasses based on some input.
  • You want to promote loose coupling by depending on abstractions rather than concrete implementations.

Wrapping Up

The Factory Pattern is a powerful tool that can help you write cleaner, more maintainable code by abstracting object creation. It’s particularly useful in scenarios where you have multiple subclasses or complex instantiation logic.

I hope this gives you a good starting point to explore the Factory Pattern in your own projects. If you’ve used it before or have any thoughts, feel free to share in the comments—I’d love to hear your experiences!

Happy coding!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player