// Design Patterns · 03
Factory Method
Let one method decide which concrete class gets created.
How it fits together
- NotificationService uses / calls NotificationSenderFactory (asks)
- NotificationSenderFactory creates NotificationSender (returns one)
- NotificationService uses / calls NotificationSender (sends via)
- EmailSender implements NotificationSender
- SmsSender implements NotificationSender
Spot it
- new ConcreteClass() scattered everywhere
- Choosing the type needs config or runtime data
- Tests cannot swap what a class creates
It costs
An extra hop between the caller and the class it actually gets.
In code
public interface INotificationSender{ Task SendAsync(string message, CancellationToken ct);}public interface INotificationSenderFactory{ INotificationSender Create(string channel);}public sealed class NotificationSenderFactory(EmailSender email, SmsSender sms) : INotificationSenderFactory{ public INotificationSender Create(string channel) => channel switch { "email" => email, "sms" => sms, _ => throw new ArgumentOutOfRangeException(nameof(channel), channel, null), };}public sealed class NotificationService(INotificationSenderFactory factory){ public Task NotifyAsync(User user, string message, CancellationToken ct) { var sender = factory.Create(user.Channel); return sender.SendAsync(message, ct); }}Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
In the original pattern, a base class declares a method that returns a product, and subclasses override it to choose the concrete type. In everyday use, it is any method that owns the decision of what to instantiate.
A factory that only wraps construction with no decision adds nothing. A factory that takes the DI container and resolves anything has become a service locator, which hides dependencies again.
The DI container is the biggest factory in most apps. Many frameworks also ship factories for objects with lifetimes or pooling the container cannot express alone, such as HTTP clients and loggers. Abstract Factory is the sibling that creates whole families of related objects.
Ask for it
- factory method
- creator / product
- abstract factory
- function factory
- client factory
NotificationService creates an EmailSender or SmsSender based on user settings. Introduce a NotificationSenderFactory with Create(channel) returning a NotificationSender, register it in the DI container, and let tests substitute a fake factory.