Hook
Catchy Title Options
Clean Object Creation in C#: Mastering the Factory Pattern
Stop Using
newEverywhere: Decouple Your C# Code with Factory Pattern
The Hook
Explain how sprawling new keywords scattered across a codebase create tight coupling, fragile architecture, and unit testing headaches.
Objective: Show how encapsulating instantiation logic makes your C# applications extensible, maintainable, and aligned with SOLID principles.
The Problem: Direct Instantiation & Tight Coupling
Scenario
A notification system sending alerts via Email, SMS, or Push.
Code Example (The Anti-Pattern)
Show client code directly instantiating new EmailService(), new SmsService(), etc., inside conditional branches.
Why it Breaks Down
Violates Open/Closed Principle (OCP): Adding a new channel forces modification of caller logic.
Dependency Rigidity: Hardcoding class names makes swapping implementations or mocking for unit tests difficult.
Leaky Abstractions: Configuration parameters (API keys, SMTP settings) leak into calling code.
Core Mechanics of the Factory Pattern
Plain-English Definition
A creational pattern that provides an interface for creating objects in a superclass, allowing subclasses to alter the type of objects that will be created.
Real-World Analogy
A custom furniture workshop. You order a "Chair"—you don't build it yourself; the workshop handles raw materials, assembly, and quality checks, delivering a finished IChair.
Real-World Scenario: Notification Delivery System
In an application, you often need to send notifications via different channels (Email, SMS, Push Notification). Using new EmailService() directly across your application tightly couples code to specific implementations and makes it difficult to add new notification channels.
The Factory Pattern delegates object creation to specialized subclass factories, encapsulating instantiation logic.
1. Product Interface & Concrete Products
Defines the contract for notification channels and implements specific delivery mechanisms.
// Product Interface
public interface INotification
{
Task SendAsync(string recipient, string message);
}
// Concrete Product A: Email
public class EmailNotification : INotification
{
public async Task SendAsync(string recipient, string message)
{
await Task.Delay(50); // Simulate SMTP API call
Console.WriteLine($"[Email] Sent to {recipient}: {message}");
}
}
// Concrete Product B: SMS
public class SmsNotification : INotification
{
public async Task SendAsync(string recipient, string message)
{
await Task.Delay(50); // Simulate Twilio API call
Console.WriteLine($"[SMS] Sent to {recipient}: {message}");
}
}
// Concrete Product C: Push
public class PushNotification : INotification
{
public async Task SendAsync(string recipient, string message)
{
await Task.Delay(50); // Simulate Firebase API call
Console.WriteLine($"[Push] Sent to device {recipient}: {message}");
}
}

Join the conversation! Your thoughts help the community grow.