Factory pattern: centralise object creation
The caller asks a factory for an interface. The factory chooses which concrete object to create, keeping construction decisions in one place.
Watch it animatedStep through it, with the running line highlighted. Free with an account.The idea
The factory owns the decision about which notifier class to instantiate. The caller works with INotifier and does not need a switch or direct construction scattered across the application.
Code
class Notifier {
public:
virtual string send(const string& message) = 0;
virtual ~Notifier() = default;
};
class EmailNotifier : public Notifier {
public:
string send(const string& message) override { return "Email: " + message; }
};
class SmsNotifier : public Notifier {
public:
string send(const string& message) override { return "SMS: " + message; }
};
unique_ptr<Notifier> createNotifier(const string& channel) {
if (channel == "email") return make_unique<EmailNotifier>();
if (channel == "sms") return make_unique<SmsNotifier>();
throw invalid_argument("unknown channel");
}
int main() {
unique_ptr<Notifier> notifier = createNotifier("email");
cout << notifier->send("Order shipped") << "\n";
}from typing import Protocol
class Notifier(Protocol):
def send(self, message: str) -> str: ...
class EmailNotifier:
def send(self, message: str) -> str: return "Email: " + message
class SmsNotifier:
def send(self, message: str) -> str: return "SMS: " + message
def create_notifier(channel: str) -> Notifier:
match channel:
case "email": return EmailNotifier()
case "sms": return SmsNotifier()
case _: raise ValueError(channel)
notifier = create_notifier("email")
print(notifier.send("Order shipped"))interface INotifier {
string Send(string message);
}
class EmailNotifier : INotifier {
public string Send(string message) => "Email: " + message;
}
class SmsNotifier : INotifier {
public string Send(string message) => "SMS: " + message;
}
static class NotifierFactory {
public static INotifier Create(string channel) => channel switch {
"email" => new EmailNotifier(),
"sms" => new SmsNotifier(),
_ => throw new ArgumentOutOfRangeException(nameof(channel))
};
}
INotifier notifier = NotifierFactory.Create("email");
Console.WriteLine(notifier.Send("Order shipped"));Tips
- This example is a simple factory: a small method centralises a creation choice. Larger Factory Method and Abstract Factory patterns address different creation needs.
- A factory is useful when the choice depends on configuration, input, or a family of related objects. For one uncomplicated object, direct construction is simpler.
In the .NET library
A dependency-injection container can act as a factory too. Use a custom factory when creation needs runtime input, such as the selected notification channel.