Factory Method
A factory method makes an object for you, so the code that uses it doesn't name the class it gets. A simple factory picks the class in one switch, instead of in every caller; the Gang of Four's Factory Method lets each subclass decide what to create. ASP.NET Core's registrations with a lambda are factories too.
Journeyman · C# · OOP · Updated
After this lesson you can
- Hide which class is created behind a factory method
Key takeaways
- A factory method makes an object for its callers, so they ask for an
INotifierand never saynew EmailNotifier(). - A simple factory keeps the
switchthat picks the class in one place, so a new channel is one new class and one new line. - In the Gang of Four's Factory Method, a base class does the work, and an
abstractmethod lets each subclass decide what to create. AddScoped<IUserDao>(_ => new UserDao(connectionString))is a factory: ASP.NET Core calls the lambda whenever a controller needs anIUserDao.
Northwind sends an order confirmation, by email or by text message, whichever the customer chose. Two classes do the sending, behind one interface:
INotifier notifier = new EmailNotifier();
Console.WriteLine(notifier.Send("Your order 10248 has shipped."));
notifier = new TextNotifier();
Console.WriteLine(notifier.Send("Your order 10248 has shipped."));
public interface INotifier
{
string Send(string message);
}
public class EmailNotifier : INotifier
{
public string Send(string message) => quot;Email: {message}";
}
public class TextNotifier : INotifier
{
public string Send(string message) => quot;Text: {message}";
}
Email: Your order 10248 has shipped.
Text: Your order 10248 has shipped.
The problem
Somewhere, code has to decide which class to create. Left alone, that decision ends up wherever a notifier is needed:
INotifier notifier;
if (customer.Channel == "email")
{
notifier = new EmailNotifier();
}
else
{
notifier = new TextNotifier();
}
The order page has that if. So do the shipping code and the password reset. When a third channel arrives, an app notification, you must find and change every copy, and missing one is a bug.
A factory method
A factory method is a method whose job is to make an object. The decision moves into one place:
INotifier notifier = NotifierFactory.Create("email");
Console.WriteLine(notifier.Send("Your order 10248 has shipped."));
Console.WriteLine(NotifierFactory.Create("text").Send("Your table is ready."));
try
{
NotifierFactory.Create("pigeon");
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message);
}
public static class NotifierFactory
{
public static INotifier Create(string channel) => channel switch
{
"email" => new EmailNotifier(),
"text" => new TextNotifier(),
_ => throw new ArgumentException( quot;There's no notifier for {channel}.")
};
}
public interface INotifier
{
string Send(string message);
}
public class EmailNotifier : INotifier
{
public string Send(string message) => quot;Email: {message}";
}
public class TextNotifier : INotifier
{
public string Send(string message) => quot;Text: {message}";
}
Email: Your order 10248 has shipped.
Text: Your table is ready.
There's no notifier for pigeon.
- The callers ask for an
INotifier, and never saynew EmailNotifier(). They don't know, or care, which class they get. - The
switchlives in one place. A new channel is one new class and one new line. - An unknown channel fails loudly, in one place, with a clear message.
This "simple factory" is the version you'll write most often.
The Gang of Four's Factory Method
The Gang of Four's Factory Method pattern goes one step further: a base class does the work, and leaves one decision, what to create, to its subclasses.
OrderConfirmation confirmation = new EmailConfirmation();
Console.WriteLine(confirmation.Confirm(10248));
confirmation = new TextConfirmation();
Console.WriteLine(confirmation.Confirm(10249));
public abstract class OrderConfirmation
{
// The factory method: each subclass decides which notifier to make.
protected abstract INotifier CreateNotifier();
public string Confirm(int orderId)
{
INotifier notifier = CreateNotifier();
return notifier.Send( quot;Thanks! Order {orderId} is confirmed.");
}
}
public class EmailConfirmation : OrderConfirmation
{
protected override INotifier CreateNotifier() => new EmailNotifier();
}
public class TextConfirmation : OrderConfirmation
{
protected override INotifier CreateNotifier() => new TextNotifier();
}
public interface INotifier
{
string Send(string message);
}
public class EmailNotifier : INotifier
{
public string Send(string message) => quot;Email: {message}";
}
public class TextNotifier : INotifier
{
public string Send(string message) => quot;Text: {message}";
}
Email: Thanks! Order 10248 is confirmed.
Text: Thanks! Order 10249 is confirmed.
Confirmis written once, in the base class: it makes a notifier, and sends the message.CreateNotifieris the factory method:abstract, so each subclass must say what to create.- It's polymorphism, as in Adding Abstraction: the base class calls a method, and the subclass's version runs.
Use the simple factory when one method can choose. Use the subclass version when the classes doing the work differ in more than one way, and the "what to create" is one of those ways.
You've used factories already
In Program.cs, this registration is a factory:
builder.Services.AddScoped<IUserDao>(_ => new UserDao(connectionString));
The lambda is a factory method. ASP.NET Core calls it whenever a controller needs an IUserDao, and the controller never says new UserDao(...). In the tests, a different factory hands out a FakeUserDao, and the controller can't tell the difference. That's the whole point of the pattern.
Check yourself
1 question to try on the lesson page.
So what
A factory method makes objects so callers don't have to name their classes. Put the choice in one place, as a simple factory, or let subclasses make it, as the Gang of Four's Factory Method, and adding a new kind becomes one class and one line.