The Factory Method is a creational design pattern that provides a way to create objects without specifying their exact class. Instead, the creation logic is encapsulated in a method, allowing subclasses to determine the type of object that will be instantiated.
When to Use the Factory Method Pattern?
- When the exact type of object to create is determined at runtime.
- When object creation is complex and should be handled in specific subclasses.
- When you want to allow different types of objects to be created without modifying client code.
Key Features of the Factory Method Pattern
- Encapsulation of Object Creation: The Factory Method pattern allows you to encapsulate the object creation logic in a separate class (factory) and decouple it from the client code that uses the objects.
- Flexibility: The pattern allows you to change the class of the object being created without affecting the client code.
- Extensibility: You can easily add new types of objects without modifying existing code. Instead, you just add new subclasses of the factory or concrete product classes.
- Centralized Object Creation: Factory Methods centralize object creation in a single place (the factory class), making the code easier to manage and maintain.
What are the Pros and Cons?
Pros
- Separation of Concerns: The client code doesn’t need to worry about the specific class of the object, making code cleaner and more maintainable.
- Flexibility: It allows you to easily change the type of objects returned without changing the client code.
- Easy Object Creation: Objects can be created dynamically based on certain conditions, simplifying the instantiation logic.
- Decoupling of Object Creation and Usage: The object’s usage is separate from its creation, leading to more modular and testable code.
Cons
- Increased Complexity: Introducing a Factory pattern can add additional layers of complexity, especially when it’s not needed or when the system is simple.
- Overhead of Adding Factories: The pattern adds extra classes and methods, which might be unnecessary for small systems with simple object creation.
- Difficulty in Refactoring: If you later decide that the factory is unnecessary, refactoring can be difficult, especially in large systems.
Example: Car Manufacturing
Imagine you are running a car factory. There are several types of cars you can produce, such as SUVs, Saloons, and Trucks. While you know that all cars have a general structure, each type of car has its own set of features and specifications. You don’t want every customer or employee of the factory to know how to build every specific type of car. Instead, you create a Factory Method for each car type. You can have a general CarFactory
that provides a CreateCar()
method, but the actual car type (SUV, Saloon, Truck) is determined by subclasses of this factory, which specify how each car should be built.
Class Diagram
Implementation in C#
Let’s see how we can use the Factory Method design pattern in practice. Consider the car manufacturing scenario
///
/// Abstract product (Car)
///
public interface ICar
{
public void GetCarType();
}
///
/// Concrete product 1 (SUV)
///
public class SUV : ICar
{
public void GetCarType()
{
Console.WriteLine("This is an SUV.");
}
}
///
/// Concrete product 2 (Saloon)
///
public class Saloon : ICar
{
public void GetCarType()
{
Console.WriteLine("This is a Saloon.");
}
}
///
/// Concrete product 3 (Truck)
///
public class Truck : ICar
{
public void GetCarType()
{
Console.WriteLine("This is a Truck.");
}
}
///
/// Creator (CarFactory) - Abstract class with factory method
///
public abstract class CarFactory
{
///
/// Factory Method - Returns the product
///
public abstract ICar CreateCar();
}
///
/// Concrete Creator 1 - SUV Factory
///
public class SUVFactory : CarFactory
{
///
/// Create an SUV car
///
public override ICar CreateCar()
{
return new SUV();
}
}
///
/// Concrete Creator 2 - Saloon Factory
///
public class SaloonFactory : CarFactory
{
///
/// Create a Saloon car
///
public override ICar CreateCar()
{
return new Saloon();
}
}
///
/// Concrete Creator 3 - Truck Factory
///
public class TruckFactory : CarFactory
{
///
/// Create a Truck car
///
public override ICar CreateCar()
{
return new Truck();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Lets build some cars...");
CarFactory suvFactory = new SUVFactory();
ICar suv = suvFactory.CreateCar();
suv.GetCarType(); // Output: This is an SUV.
CarFactory saloonFactory = new SaloonFactory();
ICar saloon = saloonFactory.CreateCar();
saloon.GetCarType(); // Output: This is a Saloon.
CarFactory truckFactory = new TruckFactory();
ICar truck = truckFactory.CreateCar();
truck.GetCarType(); // Output: This is a Truck.
}
}
Here is the output from the application
Lets build some cars...
This is an SUV.
This is a Saloon.
This is a Truck.
Explanation of the Code
- Car (Product): The
Car
class is an interface that defines the methodGetCarType()
. This is a contract for all types of cars that can be created by the factory. - SUV, Saloon, and Truck (Concrete Products): These are the concrete implementations of the
Car
class. Each car has its own implementation ofGetCarType()
to describe the type of car. - CarFactory (Creator): This is an abstract class with a
CreateCar()
factory method that returns aCar
object. TheCreateCar()
method is overridden by each subclass to create specific types of cars. - SUVFactory, SaloonFactory, and TruckFactory (Concrete Creators): These are the concrete factory classes, each responsible for creating a specific type of car.
- Client Code (Program): The client code interacts with the factory classes to get the appropriate car type. It doesn’t need to know how the car is created, only that it can call the factory method (
CreateCar()
) and get the car object.
Summary
The Factory Method pattern is useful when you want to allow subclasses to define the type of object that will be created. By providing a common interface to create objects, the pattern decouples the client from the concrete classes, allowing easier extension and maintenance of code.