This article explores the Factory Method design pattern, illustrating its use in managing object creation for different payment methods within an e-commerce system. It covers the classic GoF definition, a pragmatic 'simple factory' variant, and modern .NET keyed services for dynamic dependency injection, highlighting how this pattern enhances flexibility and maintainability in software architecture.
Read original on Dev.to #architectureThe Factory Method pattern is a creational design pattern that addresses the problem of creating objects without specifying the exact class of object that will be created. It achieves this by defining an interface for creating an object, but letting subclasses decide which class to instantiate. This pattern is crucial for building flexible and maintainable systems, especially when dealing with multiple concrete implementations of an interface, such as different payment processors in an e-commerce application.
Without patterns like Factory Method, code often becomes tightly coupled to concrete implementations. For example, processing payments might involve `if-else` or `switch` statements that directly instantiate `CreditCardProcessor`, `PayPalProcessor`, etc. This approach leads to code that is difficult to maintain, hard to extend when new payment methods are added, and violates the Open/Closed Principle.
public void ProcessPayment(string paymentType, decimal amount) {
if (paymentType == "CreditCard") {
var processor = new CreditCardProcessor();
processor.Process(amount);
}
else if (paymentType == "PayPal") {
var processor = new PayPalProcessor();
processor.Process(amount);
}
// ... many more payment methods, leading to rigid code
}The traditional Factory Method pattern separates the 'product' (e.g., `IPaymentProcessor`) from its 'creator'. It involves four main roles:
Architectural Benefit
This structure promotes loose coupling, allowing the client code to work with the `Product` interface rather than concrete implementations. It makes the system more extensible, as new concrete products and creators can be added without modifying existing client code.
While the classic pattern provides robust decoupling, it can lead to a proliferation of classes (one creator per product). Pragmatic variations, like a simple factory with a static method, centralize the creation logic using conditional statements (e.g., switch expressions in C#). Modern dependency injection containers, especially with features like keyed services (.NET 8+), offer another powerful way to implement factory-like behavior by registering multiple implementations of an interface under different keys, allowing for flexible resolution at runtime without explicit factory classes for each product.