π Factory Method
Factory Method is a customized assembly line for objects, usually referred to as products. It is a creational design pattern pattern which provides a unified interface for creating objects in a master class. Subclasses can define the types of objects that are being created.
π Real-World Analogyβ
Imagine youβre turning your passion for baking into a business and opening a pastry shop in Jakarta.
Without design patterns, you (representing the business logic) would handle and fulfill all the orders yourself. That can be very strenuous, especially considering you want to sell a huge variety of products.
Using the Factory Method, you would have a specific Creator for each type of Product.
Letβs say you want to sell Croissants, Muffins, and Cheesecakes. Following this pattern, you decide to hire three specialized bakers right away, even though itβs against common advice.
Now that you staffed up, you can delegate tasks. Your job is to take customer orders and assign them to the appropriate baker based on the order.
This approach makes your pastry shop scalable. For any new product, you just need to hire another specialized baker instead of learning to bake it yourself. Your main task is to manage and delegate.
π€ Problemβ
Imagine that we are building a logistics and delivery management application for our pastry shop. At its initial release, the app can handle transport by car, so the majority of business logic resides inside the Car class.
After a while, your shop becomes more popular, and we need to introduce more delivery options. Each day, customers demand Grab, Uber or even Self-Pickup.
Thatβs good for the business, but our application is struggling. Most code is bound to the Car class. Adding support for Grab would require changes to the entire codebase. All these changes are required again if new changes are requested.
In the end, we end up with an unmaintainable solution with extremely interdependent code full of conditionals that switch the solutionβs behavior.
π‘ Solutionβ
The Factory Method pattern is a design principle that encourages developers to use a special factory method (e.g. createDeliveryMethod) instead of directly constructing objects with new.
The change may look like we just moved the constructor call from one part of the code to another. However, we can now override the factory method in subclasses and change the type of DeliveryMethod being created.
But: Subclasses can return different types, but only if they share a common base class or an interface. The factory method in the base class Delivery should have its return type declared as an interface.
In this example, both Car and Pickup should implement the DeliveryMethod interface, which declares a method called Deliver. Each class has its own implementation of said method.
The code that utilized the factory method now doesnβt see a difference between the objects returned by various subclasses. The code handles all objects as aninterface of type DeliveryMethod. The client knows all objects are supposed to implement Deliver but doesnβt care about its inner workings.
ποΈ Structureβ
- Creator defines a Factory Method that returns objects matching the Product interface. This method can be forced through an abstract Creator class or interface to ensure consistency and modularity in code.
- Creator Implementations override the createProduct method to generate different types of objects that conform to the Product interface.
- Product interface is a standard interface used to define the commonalities of objects created by the Creator and its subclasses.
- Product Implementations are unique implementations of the Product interface that define specific behaviors and properties of an object.
// Define the Product interface
interface Product {
void PrintProduct();
}
// Define the ConcreteProductA that implements Product interface
class ConcreteProductA : Product {
public void PrintProduct() {
Console.WriteLine("This is Concrete Product A");
}
}
// Define the ConcreteProductB that implements Product interface
class ConcreteProductB : Product {
public void PrintProduct() {
Console.WriteLine("This is Concrete Product B");
}
}
// Define the Creator interface that defines the Factory Method
interface Creator {
Product CreateProduct();
}
// Define the Concrete Creator A that implements Creator interface
class ConcreteCreatorA : Creator {
public Product CreateProduct() {
return new ConcreteProductA();
}
}
// Define the Concrete Creator B that implements Creator interface
class ConcreteCreatorB : Creator {
public Product CreateProduct() {
return new ConcreteProductB();
}
}
// Application code to use the Factory pattern to create different objects
class Application {
static void Main() {
Creator creatorA = new ConcreteCreatorA();
Creator creatorB = new ConcreteCreatorB();
// Use the Factory Method to create Concrete Product A
Product productA = creatorA.CreateProduct();
productA.PrintProduct();
// Use the Factory Method to create Concrete Product B
Product productB = creatorB.CreateProduct();
productB.PrintProduct();
}
}
Pros & Consβ
β Prosβ
β Decoupling. Avoid tight coupling between business logic and object creation by separating creator and products.
β Single Responsibility Principle. Object creation is centralized in one part of the application, making it easy to maintain.
β Open/Closed Principle. New products can be introduced into the code without requiring breaking changes.
β Consβ
β Might overcomplicate things. A lot of new classes need to be introduced into the code.
π Relation with other patternsβ
- Abstract Factory, Prototype, or Builder patterns offer more flexibility but can be more complicated then Factory Method.
- Abstract Factory uses a set of Factory Methods. Prototype can be used to compose these methods.
- Factory Method can be used with Iterator to make collection subclasses return different types of compatible iterators.
- Prototype doesnβt have inheritance-related drawbacks but requires a complicated initialization process for cloned objects. Factory Method is based on inheritance but doesnβt have initialization steps.
- Factory Method is a specialization of Template Method pattern and can also serve as a step in a large Template Method.
- Abstract Factory is a generalization of Factory Method, allowing for the creation of multiple families of related objects.
- Singleton Pattern can be implemented using the Factory Method pattern, as the Factory can be designed to only create a single instance of the object.
- Builder Pattern can be used along with the Factory Method pattern to create complex objects.
- Strategy Pattern can be used in conjunction with Factory Method, with the Factory selecting the appropriate strategy to use for a given task.