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 PDFThe 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
EasyLet'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 anINotificationinterface in theNotificationsnamespace with a single methodSend(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 aNotificationFactoryclass in the same namespace with aCreate(string type)method that returns the appropriateINotificationbased on the type string:"email"returns anEmailNotification"sms"returns anSmsNotification"push"returns aPushNotification
EmailNotificationas 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 minutesNotice 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 PDFThe 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
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List10Design Patterns Part 1
Intro to Design PatternsThread-Safe SingletonFactory PatternObserver Pattern (Events)Strategy Pattern2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator