Menu
Coddy logo textTech

Factory Pattern

Part of the Object Oriented Programming section of Coddy's C# journey — lesson 53 of 70.

The Factory pattern is a creational pattern that delegates object creation to a separate method or class. Instead of using new directly in your code, you ask a factory to create objects for you. This centralizes creation logic and makes your code more flexible.

Consider a scenario where you need to create different types of documents. Without a factory, your code becomes tightly coupled to specific classes:

// Without Factory - scattered creation logic
IDocument doc;
if (type == "pdf")
    doc = new PdfDocument();
else if (type == "word")
    doc = new WordDocument();

A factory encapsulates this decision-making in one place:

public interface IDocument
{
    void Open();
}

public class PdfDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening PDF");
}

public class WordDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening Word");
}

public class DocumentFactory
{
    public IDocument Create(string type)
    {
        return type switch
        {
            "pdf" => new PdfDocument(),
            "word" => new WordDocument(),
            _ => throw new ArgumentException("Unknown type")
        };
    }
}

Now client code simply requests what it needs without knowing the concrete classes:

var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open();  // Opening PDF

The Factory pattern shines when you need to add new types later. Adding a SpreadsheetDocument requires changes only in the factory, not everywhere documents are created. This separation makes your codebase easier to maintain and test.

challenge icon

Challenge

Easy

Let's build a notification system using the Factory pattern. Instead of creating different notification types directly throughout your code, you'll centralize the creation logic in a factory class that decides which notification to create based on a simple string identifier.

You'll organize your code across three files:

  • Notification.cs: Define an INotification interface in the Notifications namespace with a single method Send(string message) that returns a string. Then create three classes that implement this interface:
    • EmailNotification - returns "Email: {message}"
    • SmsNotification - returns "SMS: {message}"
    • PushNotification - returns "Push: {message}"
  • NotificationFactory.cs: Create a NotificationFactory class in the same namespace with a Create(string type) method that returns the appropriate INotification based on the type string:
    • "email" returns an EmailNotification
    • "sms" returns an SmsNotification
    • "push" returns a PushNotification
    For any unrecognized type, return an EmailNotification as the default.
  • Program.cs: Bring everything together by creating a factory instance and using it to create notifications based on input. The factory handles all the decision-making about which concrete class to instantiate.

You will receive two inputs:

  • The notification type (e.g., sms)
  • The message to send (e.g., Your order has shipped)

Use your factory to create the appropriate notification type, then call its Send method with the message and print the result.

For example, if the inputs are push and Meeting in 5 minutes, the output should be:

Push: Meeting in 5 minutes

Notice how your main code never uses new EmailNotification() or similar - it simply asks the factory for what it needs. If you later add a SlackNotification, you'd only need to update the factory, not every place that creates notifications!

Cheat sheet

The Factory pattern is a creational pattern that delegates object creation to a separate method or class, centralizing creation logic and making code more flexible.

Without a factory, object creation is scattered and tightly coupled:

// Without Factory - scattered creation logic
IDocument doc;
if (type == "pdf")
    doc = new PdfDocument();
else if (type == "word")
    doc = new WordDocument();

With a factory, creation logic is encapsulated in one place:

public interface IDocument
{
    void Open();
}

public class PdfDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening PDF");
}

public class WordDocument : IDocument
{
    public void Open() => Console.WriteLine("Opening Word");
}

public class DocumentFactory
{
    public IDocument Create(string type)
    {
        return type switch
        {
            "pdf" => new PdfDocument(),
            "word" => new WordDocument(),
            _ => throw new ArgumentException("Unknown type")
        };
    }
}

Client code requests objects without knowing concrete classes:

var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open();  // Opening PDF

The Factory pattern makes adding new types easier - changes are isolated to the factory, not scattered throughout the codebase.

Try it yourself

using System;
using Notifications;

class Program
{
    public static void Main(string[] args)
    {
        // Read input
        string type = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: Create a NotificationFactory instance

        // TODO: Use the factory to create the appropriate notification

        // TODO: Call the Send method and print the result
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming