CREATIONAL PATTERN - Factory pattern
Hari Krishnan
Posted on April 28, 2021
What is factory ?
As the name suggests, factory is nothing but the manufacturer of a product. Client requests a product and the factory delivers it.
For example : If a client requires a chair, factory delivers the chair to the client. You can have one more wrapper in between, the dealer. But for basic understanding let us consider the manufacturer delivers the chair to the client.
Why should you use Factory pattern ?
The client consuming the product need not know how the chair is manufactured, how it is delivered etc..., they just need the chair to be delivered. So, in OOP also, we need to hide the creation logic of classes from the consumers.
Types of factory pattern
- Simple factory pattern
- Factory method pattern
- Abstract factory pattern
1. Simple factory pattern
Simple factory pattern is nothing but moving the creation of object one level up to a factory method instead of creating the object with the new keyword.
In simple factory, we have a factory class which has a method which will return different types of objects based on user input. In the below example ; when the consumer requires a chair; it returns the chair instance and similarly for other furniture's as well. A simple switch case will do the job of returning objects based on user input.
// PRODUCT
abstract class Furniture {
public abstract void use();
}
// CONCRETE PRODUCT 1
class Chair : Furniture {
public override void use() {
Console.Writeline('Using a chair');
}
}
// CONCRETE PRODUCT 2
class Table : Furniture {
public override void use() {
Console.Writeline('Using a table');
}
}
enum FurnitureType {
Chair,
Table
}
// SIMPLE FACTORY METHOD
class FurnitureFactory {
public Furniture createProduct(FurnitureType type) {
switch(type) {
case FurnitureType.Chair:
return new Chair();
case FurnitureType.Table:
return new Table();
default:
return new Chair();
}
}
}
static void Main(string[] args) {
FurnitureFactory creator = new FurnitureFactory();
Furniture furniture = creator.createProduct(FurnitureType.Chair);
furniture.use();
}
Problem without factory pattern :
Client would require a chair and he would call the product class in the following way :
Chair chair = new Chair(ManufacturedIn.India, Seat.Leather);
Here the client needs to specify the information required to manufacture the chair. But this is not a good practice. Instead with minimal parameters like just furniture type alone, the client can pass it and get access to the product. We can handle it in a switch case in the factory method if required.
Limitations with Simple factory pattern
Let's say in future if the manufacturer is going to sell sofas. We will have to modify the existing createProduct method by adding a new switch case which will return the Sofa object.
According to open-closed principle
in the SOLID principles, a class must be open for extension
and should be closed for modification
.
To overcome the limitations by the simple factory pattern, the Factory Method Pattern was introduced.
2. Factory method pattern
It defines an interface or an abstract class for creating an object, but lets subclasses decide which class to instantiate.
This abstract class is called the Creator
and the subclasses which are responsible for instantiation are called Concrete creators.
Now the point is each product will have a concrete creator. So we need not go to the simple factory method and modify the existing code to support creation of new products.
There are four important terms to know :
- Product
- Concrete Product
- Creator (Factory class which has the abstract createProduct() method)
- Concrete Creator (ChairFactory, TableFactory and SofaFactory)
Product (Furniture)
Here the product is the Furniture. Since we also have specific product types under furniture like chair, table etc.. We define an abstract class called Furniture.
Concrete Product (Chair,Table, Sofa)
Chair, Table and Sofa are the concrete products here and they are extended from the Product class and their methods are overridden here.
Creator (FurnitureFactory)
This is the abstract class which contains the factory method createProduct()
which is responsible for creating the Furniture and delivering it to us.
Concrete creator (ChairFactory, TableFactory and SofaFactory)
Concrete creators are the ChairFactory and the TableFactory which inherits the Creator abstract class (FurnitureFactory) and overrides the createProduct method in order to create the individual products.
Note : Abstraction can be done by using both interface or abstract classes.
Implementation for the Factory method pattern :
// PRODUCT
abstract class Furniture {
public abstract void use();
}
// CONCRETE PRODUCT 1
class Chair : Furniture {
public override void use() {
Console.Writeline('Using a chair');
}
}
// CONCRETE PRODUCT 2
class Table : Furniture {
public override void use() {
Console.Writeline('Using a table');
}
}
// CREATOR
abstract class FurnitureFactory {
public abstract Furniture createProduct();
}
// CONCRETE CREATOR 1
class ChairFactory: FurnitureFactory {
public override Furniture createProduct() {
return new Chair();
}
}
// CONCRETE CREATOR 2
class TableFactory: FurnitureFactory {
public override Furniture createProduct() {
return new Table();
}
}
static void Main(string[] args) {
FurnitureFactory creator = new ChairFactory();
Furniture furniture = creator.createProduct();
furniture.use();
}
Advantages of using the factory method
So whenever a new requirement arises, you need not change the existing code, instead you are extending or adding additional code.
For most applications, when code is modified you will have to re-run the unit tests again. But when it is extended you don't have to worry about the parent products functionalities.
It also provides one more level of abstraction to the createProduct() factory method, without knowing what type of object is being created i.e the implementation details of how the object is instantiated is also hidden from the client.
3. Abstract Factory Pattern
It is a super factory
which creates other factories. It provides a wrapper interface for creating objects that belong to a group(related or dependent objects)
, without specifying their concrete classes (inherited subclasses which has the actual object creation logic).
In our example, the concrete products chair, table and sofa; all can have several variants like Ancient, Modern etc...
So for each variant we will be having a ConcreteFactory class in the normal Factory method; which will be responsible for object creation. Consider there are more than 10 variants, we will have totally 30 ConcreteFactory classes for object creation like AncientChairFactory, ModernChairFactory etc...
Mostly furniture vendor's update the items very frequently, so we should also not modify the existing code. Keeping all theses things in mind, the abstract pattern would help in these kinds of scenarios.
Participants in the abstract factory
- AbstractFactory
- ConcreteFactory
- Abstract Product
- Product
- Client
Implementation :
// ABSTRACT FACTORY CLASS
abstract class FurnitureFactory {
public abstract Furniture createChair();
public abstract Furniture createTable();
}
// CONCRETE FACTORY 1
class NormalFurnitureFactory: FurnitureFactory {
public override void createChair() {
return new Chair();
}
public override void createTable() {
return new Table();
}
}
// CONCRETE FACTORY 2
class ModernFurnitureFactory: FurnitureFactory {
public override void createChair() {
return new ModernChair();
}
public override void createTable() {
return new ModernTable();
}
}
// CONCRETE FACTORY 3
class AncientFurnitureFactory: FurnitureFactory {
public override void createChair() {
return new AncientChair();
}
public override void createTable() {
return new AncientTable();
}
}
// ABSTRACT PRODUCT
abstract class Furniture {
public abstract void use();
}
// PRODUCT A1
class Chair: Furniture {
public override void use() {
Console.WriteLine('Using a chair');
}
}
// PRODUCT A2
class ModernChair: Chair {
public override void use() {
Console.WriteLine('Using a modern chair');
}
}
// PRODUCT A3
class AncientChair: Chair {
public override void use() {
Console.WriteLine('Using an ancient chair');
}
}
// PRODUCT B1
class Table: Furniture {
public override void use() {
Console.WriteLine('Using a table');
}
// PRODUCT B2
class ModernTable: Table {
public override void use() {
Console.WriteLine('Using a modern table');
}
}
// PRODUCT B3
class AncientTable: Table {
public override void use() {
Console.WriteLine('Using an ancient table');
}
}
// CLIENT
static void main {
FurnitureFactory modernFactory = new ModernFurnitureFactory();
Furniture modernChair = factory.createChair();
modernChair.use();
}
To simply remember the abstract factory pattern, Chinese furniture manufacturers will have a skilled-group of professionals and they all will be present in a common factory, likewise for all the other variants, the resources and materials required for development will be grouped to a single factory.
It will not be like all factories will be capable of manufacturing all the variants and that is also not a good practise. So we create concrete factories based on the variants. They will be fully responsible for developing those kinds of products.
Summary
So the main purpose of the factory pattern is to hide the client how the object is constructed. So object creation is moved one level up to the factory method.
It is very simple now, in simple factory, the client just tells the manufacturer what end product they need, and the manufacturer delivers the product by hiding all inner details like the manufacturing process and others etc...
In Factory method pattern, we follow the open-closed principle of SOLID principles and allow only code extensibility.
When there are multiple variants of a FurnitureProduct like ancient, modern, normal, Chinese etc... , the abstract factory pattern is used.
Posted on April 28, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.