Design Patterns - Factory Method
In our previous blog of the design pattern series, we introduced you to the world of design patterns. We saw what is design patterns along with its types. We then dived into one of its types viz. creational design patterns. In this post, we will explore the Factory Method which falls under the category of creational design patterns. Let’s begin right away.
Table of contents
- Factory Method
- Conclusion
Factory Method
The factory method is used when the object creation logic is not exposed to the client. And it lets the subclass decide on what class needs to be instantiated.
Factory methods can help in creating a loosely coupled code between the creator and the products. It also helps to follow the Single Responsibility principle in object creation and the Open/Close principle which helps in adding a new set of products without making changes in any of the existing code.
Below is the template of the code.
namespace PARTECH_FactoryMethod
{
internal class Program
{
static void Main(string[] args)
{
ProductFactory product;
product = new ConcreteProductFactory("name","description");
}
}
/// <summary>
/// Abstract class of the product
/// </summary>
public abstract class Product
{
public abstract string Name { get; }
public abstract string Description { get; }
}
/// <summary>
/// Concrete Product class which implements the abstract Product class
/// </summary>
public class ConcreteProduct : Product
{
public string name;
public string description;
public ConcreteProduct(string name, string description)
{
this.name = name;
this.description = description;
}
public override string Name
{
get { return name; }
}
public override string Description
{
get { return description; }
}
}
/// <summary>
/// Creator Abstract class
/// </summary>
public abstract class ProductFactory
{
public abstract Product CreateProduct();
}
/// <summary>
/// Concrete Creator
/// </summary>
public class ConcreteProductFactory: ProductFactory
{
public string name;
public string description;
public ConcreteProductFactory(string name, string description)
{
this.name = name;
this.description = description;
}
public override Product CreateProduct()
{
return new ConcreteProduct(name, description);
}
}
}
In the above template, the code works like this -
An abstract Product class is created with a few properties that are required for the class(interface can also be used instead of abstract for the class). A concrete Product class inherits the abstract product class and implements the abstract properties provided in the base abstract class.
With this, N number of concrete product classes can be created based on the specification and needs. But it doesn't stop there. To create the concrete product classes instance, an abstract creator class is created, which holds the method with the return type of Product class.
The concrete creator class inherits the creator abstract class and creates the instance of the concrete product class. And in the main method, the concrete creator creates and provides the instance of a concrete product.
Now that have seen the template of the Factory method, let us understand the practical implementation of the Factory method with a simple scenario.
Consider a car manufacturing company that manufactures hatchbacks, sedans, and SUVs. They have an online portal for the dealers to place orders. But the procedure for placing orders remains the same for all types of cars. Internally there are certain specifications that change based on the type of the car - the plant at which it is being produced, type of vehicle required for transporting them, and so on.
Let us now see how to implement the Factory Method design pattern for this particular scenario.
Step 1 - Create a class name and name it as Car.cs (This is going to be our Product abstract class). Add the below lines inside the Car.cs file.
namespace PARTECH_FactoryMethod
{
/// <summary>
/// Product abstract class - Car is the product here
/// </summary>
public abstract class Car
{
public abstract string ModelName { get; }
public abstract string GetPlant();
public abstract string GetTransportType();
}
}
As you can see, it is an abstract class with ModelName property that contains two abstract methods - one for getting the plant and another for getting the transport type.
Next, we have the base class i.e Car. We will implement the types of cars which extend this. Create three class files and name them SUV, Hatchback, and Sedan. And add the below codes in the respective files.
namespace PARTECH_FactoryMethod
{
public class SUV : Car
{
public string Model;
public SUV(string model)
{
Model = model;
}
public override string ModelName
{
get { return Model; }
}
public override string GetPlant()
{
return "Plant C";
}
public override string GetTransportType()
{
return "Single Deck Single row truck";
}
}
}
namespace PARTECH_FactoryMethod
{
public class Hatchback : Car
{
public string Model;
public Hatchback(string model)
{
Model = model;
}
public override string ModelName
{
get { return Model; }
}
public override string GetPlant()
{
return "Plant B";
}
public override string GetTransportType()
{
return "Double Decker Triple row truck";
}
}
}
namespace PARTECH_FactoryMethod
{
public class Sedan : Car
{
public string Model;
public Sedan(string model)
{
Model = model;
}
public override string ModelName
{
get { return Model; }
}
public override string GetPlant()
{
return "Plant A";
}
public override string GetTransportType()
{
return "Double Decker Double row truck";
}
}
}
In the above codes, each type of car overrides the abstract method of Car and has its logic for Plant, Transport, and Model name (hardcoded for simplicity).
Now, we have created the Abstract Product class (Car) and Concrete Product classes (SUV, Hatchback, and Sedan).
Let's create an abstract creator class and concrete creator class and understand how the already created abstract product and concrete product class are consumed here.
To implement an abstract creator class, add a new class and name it as CarFactory. And add the below lines of code to it.
namespace PARTECH_FactoryMethod
{
public abstract class CarFactory
{
public abstract Car CreateCar();
}
}
The above abstract creator class has an abstract CreateCar method which will be extended and implemented by the Concrete Creator classes. To implement the concrete creator classes, create three class files (SUVFactory, HatchbackFactory, and Sedan Factory) and add the below lines of code in respective files.
public class SUVFactory : CarFactory
{
public string Model;
public SUVFactory(string modelName)
{
Model = modelName;
}
public override Car CreateCar()
{
return new SUV(Model);
}
}
public class HatchbackFactory : CarFactory
{
public string Model;
public HatchbackFactory(string modelName)
{
Model = modelName;
}
public override Car CreateCar()
{
return new Hatchback(Model);
}
}
public class SedanFactory : CarFactory
{
public string Model;
public SedanFactory(string modelName)
{
Model = modelName;
}
public override Car CreateCar()
{
return new Sedan(Model);
}
}
All three classes take up the model name as input and return the corresponding concrete product class.
Now, let's consume them in the main method.
static void Main(string[] args)
{
Car car = null;
Console.WriteLine("Enter the type of car required\n1. Golf\n2. S8\n3. GLC 300 ");
var carModel = Console.ReadLine();
while (carModel != null)
{
switch (carModel)
{
case "1":
{
car = new HatchbackFactory("Golf").CreateCar();
}
break;
case "2":
{
car = new SedanFactory("S8").CreateCar();
}
break;
case "3":
{
car = new SUVFactory("GLC 300").CreateCar();
}
break;
}
var plantInfo = car.GetPlant();
var transportType = car.GetTransportType();
Console.WriteLine($"Car Model - {car.ModelName}, Manufacturing Plant - {plantInfo}, Transport Type - {transportType}");
carModel = Console.ReadLine();
}
}
The console program takes input from the user and displays the model, plant, and transport type as shown below.
Conclusion
In this, we have seen what Factory Method creational design pattern is and how it can be implemented in a real-world scenario. Stay tuned for the next blog on Design Patterns.