DEV Community

Cover image for # 🏭 Deep Dive: Factory Method in .NET / C#
Megha Ugile
Megha Ugile

Posted on

# 🏭 Deep Dive: Factory Method in .NET / C#

Learn how the Factory Method pattern helps create flexible, decoupled object instantiation in your .NET apps.

📌 What is the Factory Method Pattern?

The Factory Method pattern is a creational design pattern that defines an interface (or abstract method) for creating an object, but lets subclasses decide which class to instantiate. It decouples client code from object creation logic—perfect when your system needs to support multiple product types or dynamic creation at runtime.


🤔 When to Use It

Use Factory Method when:

  • You want to delegate object creation to subclasses.
  • There are multiple product types selected by configuration or runtime data.
  • You want to follow SOLID principles like DIP and OCP.

Common scenarios include plug-in architectures, UI element generation (buttons, dialogs), and different notification types.


🧩 Pattern Components

The pattern typically involves:

  1. Product – The interface or abstract class that defines common behaviors.
  2. ConcreteProduct – Implementations of the Product interface.
  3. Creator – Declares the factory method, can also define a default implementation.
  4. ConcreteCreator – Implements the factory method to return a ConcreteProduct.

🖋 Example: Notification System

Let's implement a flexible notification system that sends different message types.

// 1. Product Interface
public interface INotification {
  void Send(string recipient, string message);
}

// 2. Concrete Products
public class EmailNotification : INotification {
  public void Send(string r, string m) =>
    Console.WriteLine($"Email: To={r}, Msg={m}");
}

public class SmsNotification : INotification {
  public void Send(string r, string m) =>
    Console.WriteLine($"SMS: To={r}, Msg={m}");
}

// 3. Creator
public abstract class NotificationCreator {
  public abstract INotification CreateNotification();

  public void Notify(string recipient, string message) {
    var note = CreateNotification();
    note.Send(recipient, message);
  }
}

// 4. Concrete Creators
public class EmailCreator : NotificationCreator {
  public override INotification CreateNotification() =>
    new EmailNotification();
}

public class SmsCreator : NotificationCreator {
  public override INotification CreateNotification() =>
    new SmsNotification();
}

Enter fullscreen mode Exit fullscreen mode

🚀 Usage at Runtime

static void Main() {
  NotificationCreator creator;

  // Switch based on config, user preference, etc.
  string type = "email";
  creator = (type == "sms") ? new SmsCreator() : new EmailCreator();

  creator.Notify("[email protected]", "Hello from Factory Method!");
}
Enter fullscreen mode Exit fullscreen mode

The client code only deals with NotificationCreator and INotification, keeping it decoupled from concrete implementations.

✅ Benefits
Loose coupling: Client code never needs to know concrete types.

Open for extension: Add new creators/products without modifying existing systems—follows OCP.

Subclass-specific logic: Additional setup/configuration can occur in Concrete Creators before returning objects.

⚠️ Common Pitfalls & Best Practices

Pitfall ** ** Solution
Overuse Apply only where variability or decoupling is needed
Over-reliance on config Use safe defaults or factory chaining
Breaking DIP Use interfaces and dependency injection

🛠 Real‐World Scenarios

Payment gateway selection (PayPal, Stripe, etc.)

UI control creation (WinForms, Web UI, mobile buttons)

Loggers or file handlers chosen at runtime

Unit testing with mock implementations

🧑‍🏫 UML Overview

Creator
+ Notify()
+ CreateNotification() → INotification

↳ EmailCreator
↳ SmsCreator

INotification ⟶ EmailNotification, SmsNotification
Enter fullscreen mode Exit fullscreen mode

🧠 How It Relates to SOLID
DIP: Client code depends on INotification, not concrete types.
OCP: Extend with new creators without changing existing code.
LSP: All concrete creators and products can replace their base types seamlessly.

🏁** Summary**
The Factory Method pattern empowers .NET developers to:
Decouple object creation logic from usage
Configure behavior dynamically
Write scalable, testable, and maintainable code

Look for repeated new keywords and rigid switch statements—they're often signs that Factory Method might be a better solution.

🔧 Further Reading
Refactoring.Guru – Factory Method
Microsoft Docs – Design Patterns
HackerNoon – Benefits of Using Factory Pattern in .NET

If you found this post helpful, give it a ❤️ or share your own Factory Method use cases below!

Top comments (0)