Inspiration
When I came across factory method pattern, it seemed easy. Since there was no proven authenticity of any article, I went through many sites, found that everyone was using different ways to implement, which made it confusing.
Lots of questions started coming to my mind, like in which scenarios it has to be used? Is there any fixed piece of code to implement this? What is the difference between factory pattern and factory method pattern, or are they the same? If instantiation of object deferred from UI code to subclass, where exactly instantiation needs to take place - inside factory or inside product?
There has been a lot of confusion among design patterns named FACTORY
, someone says factory, and some say simple factory, factory pattern, factory method, factory method design pattern, abstract factory.
So I decided to make all illustrations integrated in one article using the same kind of example so that comparing among different concepts could be simple.
Introduction
This is the second design pattern that falls under the creational design pattern defined1 by "Gang of Four".
This pattern is about deferring the object instantiation to sub class.
There is no "factory pattern" defined by GoF, but many sites (3, 4, 5, 6, 7, 8) confirm its existence, as it's most widely used useful, it was defined by "Garry Woodfine" (2, 3). So here I will try to describe all approaches related to factory method.
Who Should Read this Article?
- If you are a novice at all for design patterns.
- If you are a novice at factory method pattern.
- If you are confused between factory pattern and factory method pattern.
- If you have gone through many examples related to factor method, but still confused, because you are not able to map the code with its class diagram.
- If you want to know what each class in the pattern does and how.
About the Article
This story compares from the real world scenario to the programming world, so the story keeps on switching between both the languages. Since I tried to map coding language with real life language, you might need to map it by perusing and comparing code with explanation. Sometimes, you may feel if code could be shortened, but again it’s just for the sake of mapping with real world example. Sometimes, you won’t find any change in the same class of two different stages, but it is intentional, so that you can identify where the changes took placed.
Sufficient code comments are provided in-side the code, which describes the purpose of code. Hope you will enjoy the journey.
A Real World Example - Juicer Mixer Grinder
So the story starts from the Kitchen, a place needed by everyone! Since it’s about finding a right solution for everyday kitchen’s problem of mixing product, it’s going to be long. If you want bottoms up in one shot, it’s not the right place.
Index
Stage 1 – Simple Class Without Any Interface
Real World Scenario
Few years ago, hardly did I have a craving for omelet, so I used mix egg with the help of spoon only. So it was all good so far. Was I needed to think more than this, probably not?
Yeah…….sometimes, I also needed to mix purees or pulp, but these things could also have been done with the help of different bigger jar or cup and spoon.
I could have bought a blender, but not sure if it would had been economic for this scenario.
In Programming Terms
So sometimes, it’s good if we (as main class) instantiate an object directly without delegating the responsibility, if it requires once in a while.
Class Diagram
Implementation Code
UIClass.cs
using System;
namespace SimpleClass
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item to blend : Egg or Pulp");
string strUserInput = Console.ReadLine();
switch (Console.ReadLine())
{
case "Egg":
UseCupForEgg egg = new UseCupForEgg();
egg.BlendNMixEgg("EggLiquid");
break;
case "Pulp":
UseJarForPulp puree = new UseJarForPulp();
puree.MixMangoPulp("MilkShake");
break;
}
Console.ReadKey();
}
public class UseCupForEgg
{
public void BlendNMixEgg(string readyProduct)
{
Console.WriteLine(readyProduct + "Egg is mixed");
}
}
public class UseJarForPulp
{
public void MixMangoPulp(string readyProduct)
{
Console.WriteLine(readyProduct + "Mango shake is prepared.");
}
}
}
}
Observation on Stage 1
What I observed was that I always do the same process for mixing similar things, and type of end product is of the same type, so why can I not use the same utensil for both the processes? Can I minimize and improve the things? After all, it’s the same defined process (mixing) is going to be used to get same a type of end product which is LIQUID.
Stage 2 - No Factory But With Defined Interface
Real World Scenario
So now, I had a single JAR being used for BLENDING process to convert Egg or Pulp into LIQUID.
In Programming Terms
Single UI using same process to convert multiple raw product to same abstract product.
Class Diagram
Implementation Code
UIClass.cs
using System;
namespace NoFactory
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Egg, Pulp");
IMixedProduct endProduct = null;
switch (Console.ReadLine())
{
case "Egg":
endProduct = new EggProduct();
endProduct.BlendProduct("EggLiquid");
break;
case "Pulp":
endProduct = new PureeProduct();
endProduct.BlendProduct("MilkShake");
break;
}
Console.ReadKey();
}
}
}
Product.cs
namespace NoFactory
{
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
public class EggProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
public class PureeProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
}
Problem with Stage 2
After some time, I needed to make omelet more frequently and in more quantity for my family. So the demand increased in terms of quantity and frequency, it became more time consuming for me to blend many eggs.
Additionally, I needed to make milk shake as well or mash tomato puree sometimes. So whenever I need to grind another item, either the same utensil needed modification or else, I needed to use a different utensil (aka UI).
Let’s understand through the code.
UIClass.cs
using System;
namespace ProblemWithNoFactory
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Egg, Pulp");
IMixedProduct endProduct = null;
switch (Console.ReadLine())
{
case "Egg":
endProduct = new EggProduct();
endProduct.BlendProduct("EggLiquid");
break;
case "Pulp":
endProduct = new PureeProduct();
endProduct.BlendProduct("MilkShake");
break;
case "Sauce":
endProduct = new SauceProduct();
endProduct.BlendProduct("Sauce");
break;
case "Puree":
endProduct = new PulpProduct();
endProduct.BlendProduct("Puree");
break;
}
Console.ReadKey();
}
}
}
Product.cs
using System;
namespace ProblemWithNoFactory
{
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
public class EggProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
public class PureeProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
public class SauceProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
public class PulpProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
}
Point to Note Here
Violation of OCP – A class should be open for extension but close for modification, but it’s not here.
Requirement Analysis – (Necessity is the mother of invention)
- I needed an equipment, which can blend various semi-liquid items with speed.
- Every time, I don’t want to change the blending utensil.
- I want output in the same liquid form.
Stage 3 – Simple Factory (Also known as Factory, Factory Pattern, Factory Design Pattern)
Solution (in Real World Scenario)
So I thought of buying a blender, which can help me out with blending more eggs in less time. I could also have it use for mixing any puree to make sauce and mixing mango pulp for milk shake. So it was good for mixing the semi-liquid items only.
In Programming Terms
Here, we are delegating the responsibility of object creation to factory (Blender). And we will modify our example to follow OCP and SRP (Single class for single type of responsibility).
Definitions
"It’s a class which holds a method responsible for creation of object based on input type it receives" (aka semi-liquid -egg, puree, and mango pulp etc. in this example).
In simplest terms, Factory helps to keep all object creation in one place and avoids spreading new key value across the codebase.(3)
It creates objects without exposing the instantiation logic to the client and refers to the newly created object through a common interface. (8)
Points to Note
- It may or may not have factory interface, but it’s not a mandate.
- Factory class may or may not have
static
method, depend how you call factory at UI.
Class Diagram
Implementation Code
UIClass.cs
using System;
namespace SimpleFactoryPattern
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Egg, Pulp, Sauce, Puree");
BlenderFactory liquid = new BlenderFactory();
liquid.GetMixingProduct("SemiLiquid", Console.ReadLine());
Console.ReadKey();
}
}
}
Factory.cs
namespace SimpleFactoryPattern
{
public class BlenderFactory
{
public string GetMixingProduct(string inputType, string itemName)
{
string readyProduct = null;
if (inputType == "SemiLiquid")
{
switch (itemName)
{
case "Egg":
IMixedProduct egg = new MixtureProduct();
egg.BlendProduct(itemName);
break;
case "Pulp":
IMixedProduct pulp = new MixtureProduct();
pulp.BlendProduct(itemName);
break;
case "Sauce":
IMixedProduct sauce = new MixtureProduct();
sauce.BlendProduct(itemName);
break;
case "Puree":
IMixedProduct puree = new MixtureProduct();
puree.BlendProduct(itemName);
break;
}
}
return readyProduct;
}
}
}
Product.cs
namespace SimpleFactoryPattern
{
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
public class MixtureProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
}
Problem with Stage 3
All was good so far but one day I was in a rush and needed mashed boiled tomatoes for my curry.
Requirement Analysis – (Desire Never Ends)
So I brainstormed – I can mash boiled potatoes so shall I buy a mixer, may be it's too early, and let me think about a cheaper and economic option first. I compared similar properties of mixer and blender, both have blades & motors and both have functions that are mixing the item and both give similar output like liquid.
Stage 4 – Single Factory with Multiple Type of Responsibility (Not Advisable)
Solution (in Real World Scenario)
So I mashed the tomatoes by hand and then mixed them by blender, and wow it worked, the outcome was not that much refined as needed, but it fulfilled the essential need which was mashed tomatoes.
In Programming Terms
Here, we are calling additional method for BOILED items to prepare it before it can be used with abstract
method.
Class Diagram
Implementation Code
UIClass.cs
using System;
namespace FactoryWithMultipleResponsibility
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Liquid, or Boiled for mixing.");
string itemName = Console.ReadLine();
switch (itemName)
{
case "Liquid":
BlenderFactory liquid = new BlenderFactory();
liquid.GetMixingProduct("SemiLiquid", itemName);
break;
case "Boiled":
BlenderFactory mash = new BlenderFactory();
mash.GetMixingProduct("Boiled", itemName);
break;
}
Console.ReadKey();
}
}
}
Factory.cs
namespace FactoryWithMultipleResponsibility
{
public class BlenderFactory : MashedProduct
{
public string GetMixingProduct(string inputType, string itemName)
{
string readyProduct = null;
if (inputType == "SemiLiquid")
{
switch (inputType)
{
case "Egg":
IMixedProduct egg = new MixtureProduct();
egg.BlendProduct(itemName);
break;
case "Pulp":
IMixedProduct puree = new MixtureProduct();
puree.BlendProduct(itemName);
break;
}
}
if (inputType == "Boiled")
{
switch (inputType)
{
case "BoiledTomato":
MashedProduct mashTomato = new MashedProduct();
mashTomato.MashProduct(itemName);
IMixedProduct boildedPotato = new MixtureProduct();
boildedPotato.BlendProduct(itemName);
break;
case "BoiledPotato":
MashedProduct mashPotato = new MashedProduct();
mashPotato.MashProduct(itemName);
IMixedProduct boildedTomato = new MixtureProduct();
boildedTomato.BlendProduct(itemName);
break;
}
}
return readyProduct;
}
}
}
Product.cs
namespace FactoryWithMultipleResponsibility
{
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
public class MixtureProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
public class MashedProduct
{
public void MashProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
}
Problem with Stage 4
First, its output was not as desired, as I mashed potato by hand, second it can’t fulfill the need of making juice or grinding vegetables, etc.
In Programming Terms
Sometimes, we try to use the same class for the responsibility which it is not made for, just because it has few similar methods and properties and we want to save time and efforts.
Point to Note
Breaking SOLID SRP rule – Assigning more than one type of responsibility to a class. A class should be having a single reason to change it.
Requirement Analysis
Going excitingly on the way to explore what else usually I need in the kitchen and what can I do with this blender? And hence usually, I also needed to grind vegetables to make smoothies or grains as well to make dough, so why not have another equipment.
Stage 5 –With Multiple Factories - Less Feasible Solution
Solution (in Real World Scenario)
I decided to buy a separate mixer for all other things like solid or semi-solid items, so that I can get the desired output with other items.
In Programming Terms
I decided to delegate different responsibility to separate factories for each type of product to avoid violation of SRP.
Class Diagram
Implementation Code
UIClass.cs
using System;
namespace MultipleFactories
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Fruit, Boiled, or Grain");
string itemType = Console.ReadLine();
switch (itemType)
{
case "Fruit":
JuiceFactory juice = new JuiceFactory();
juice.GetJuiceProduct(itemType);
break;
case "Boiled":
MashingFactory semiLiquid = new MashingFactory();
semiLiquid.GetMashProduct(itemType);
break;
case "Grain":
GrindingFactory flour = new GrindingFactory();
flour.GetGrindedProduct(itemType);
break;
}
Console.ReadKey();
}
}
}
Factory.cs
namespace MultipleFactories
{
public class JuiceFactory
{
public JuiceProduct GetJuiceProduct(string inputType)
{
JuiceProduct juice = new JuiceProduct();
string readyProduct = juice.sqeezeProduct();
IMixedProduct endProduct = new MixtureProduct();
endProduct.MixProduct(readyProduct);
return juice;
}
}
public class MashingFactory
{
public MashedProduct GetMashProduct(string inputType)
{
MashedProduct mash = new MashedProduct();
string readyProduct = mash.mashProduct();
new MixtureProduct().MixProduct(readyProduct);
return mash;
}
}
public class GrindingFactory
{
public FlourProduct GetGrindedProduct(string inputType)
{
FlourProduct flour = new FlourProduct();
string readyProduct = flour.grindProduct();
new MixtureProduct().MixProduct(readyProduct);
return flour;
}
}
}
Product.cs
using System;
namespace MultipleFactories
{
public interface IMixedProduct
{
void MixProduct(string readyProduct);
}
public class MixtureProduct : IMixedProduct
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
public class JuiceProduct
{
public string sqeezeProduct()
{
return "Juice";
}
}
public class MashedProduct
{
public string mashProduct()
{
return "Mashed product";
}
}
public class FlourProduct
{
public string grindProduct()
{
return "Flour";
}
}
}
Problem with Stage 5
I thought I am almost done with my kitchen appliances, but it does not end here, after some time I needed to grind wheat to make dough. So neither was I able to use blender or mixer for grinding wheat, nor did I want to buy (aka extra effort) one more appliance in my kitchen (aka managing code).
In Programming Terms / Point to note / Requirement Analysis
- I wanted to get all grinding functions for all liquid, semi-liquid, solid items like fruits, vegetables, wheat, pulp, tomato, potato, etc.
- I don’t want to compromise with specific outcome or quality.
- I didn’t want to but many appliances, as it was neither economic, not manageable.
Stage 6 – Factory Method Pattern
(Also known as Factory method, Factory method pattern, Factory method Design pattern)
Solution
So instead of having many appliances, I decided to buy a combined JuicerMixerGrinder
having separate jars.
Real World Scenario
When you want a fruit juice, you seek a juicer, so it’s kind of a small factory (Mixer – The Creator, Jar – The Concrete creator, Egg/Fruits – Raw Product, Puree/Juice – Concrete Product). So based on your item (input) you put into the mixer, it can produce puree, shake or juice. So when Mixer blends or grinds an item, it does not know, which item it’s going to crush (aka, let the subclass decides, which class is to be instantiated).
Definition (In Programming Terms)
"Factory method pattern defines an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses."
Comparing Design Pattern Terms with Real World Example
- You – Main method
- Mixer – The factory or creator
- Jar – The concrete creator
- Item – Raw Product
Solution (In Programming Terms)
There could be 2 approaches here:
Approach 1
There could be multiple concrete factories implementing specific method depend on input type and essential (common method for all product which is mixing) method defined in factory interface to instantiate final product.
Approach 2
There could be a single concrete factory implementing product specific method depend on input type and then essential (common method for all product which is mixing) method defined in factory interface to instantiate final product.
Stage 6 (Approach 1)
Class Diagram
Implementation Code
UIClass.cs
using System;
using System;
namespace FactoryMethod_App1
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Fruit, Boiled, or Grain");
string itemType = Console.ReadLine();
switch (itemType)
{
case "Fruit":
IMixerFactory juice = new JuiceFactory();
IMixer mixJuice = juice.GetMixingProduct();
mixJuice.MixProduct(itemType);
break;
case "Boiled":
IMixerFactory boiled = new MashingFactory();
IMixer mash = boiled.GetMixingProduct();
mash.MixProduct(itemType);
break;
case "Grain":
IMixerFactory crush = new GrindingFactory();
IMixer flour = crush.GetMixingProduct();
flour.MixProduct(itemType);
break;
default :
throw new Exception("item type not supported");
}
Console.ReadKey();
}
}
}
Factory.cs
namespace FactoryMethod_App1
{
public interface IMixerFactory
{
IMixer GetMixingProduct();
}
public class JuiceFactory : IMixerFactory
{
public IMixer GetMixingProduct()
{
return new Juicer();
}
}
public class MashingFactory : IMixerFactory
{
public IMixer GetMixingProduct()
{
return new Masher();
}
}
public class GrindingFactory : IMixerFactory
{
public IMixer GetMixingProduct()
{
return new Grinder();
}
}
}
Product.cs
using System;
namespace FactoryMethod_App1
{
public interface IMixer
{
void MixProduct(string readyProduct);
}
public class Juicer : IMixer
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " juice is prepared.");
}
}
public class Masher : IMixer
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " product is mashed.");
}
}
public class Grinder : IMixer
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " flour is prepared.");
}
}
}
Stage 6 (Approach 2)
Class Diagram
Implementation Code
UIClass.cs
using System;
using System;
namespace FactoryMethod_App2
{
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Fruit, Boiled, or Grain");
IMixerFactory liquid = new MixerFactory();
IMixer endproduct = liquid.GetMixingProduct(Console.ReadLine());
Console.ReadKey();
}
}
}
Factory.cs
namespace FactoryMethod_App2
{
public interface IMixerFactory
{
IMixer GetMixingProduct(string inputType);
}
public class MixerFactory : IMixerFactory
{
public IMixer GetMixingProduct(string inputType)
{
return new Mixer(inputType);
}
}
}
Product.cs
using System;
namespace FactoryMethod_App2
{
public interface IMixer
{
string GetProductReady(string readyProduct);
}
public class Mixer : IMixer
{
public Mixer(string inputType)
{
string endproduct = GetProductReady(inputType);
Console.WriteLine(inputType + " " + endproduct + " is prepared.");
}
public string GetProductReady(string inputType)
{
string readyProduct = null;
switch (inputType)
{
case "Fruit":
readyProduct = new Juicer().sqeezeProduct();
break;
case "Boiled":
readyProduct = new Masher().mashProduct();
break;
case "Grain":
readyProduct = new Grinder().grindProduct();
break;
default :
throw new Exception("item type not supported");
}
return readyProduct;
}
}
public class Juicer
{
public string sqeezeProduct()
{
return "juice";
}
}
public class Masher
{
public string mashProduct()
{
return "mashed product";
}
}
public class Grinder
{
public string grindProduct()
{
return "flour";
}
}
}
FAQ
- Does factory pattern always need to use switch cases?
No, it’s not essential, it always depends on the need for how to manage instantiating concrete factory
- Can factory method be defined in interface only as per definition it defines an interface?
No, abstract
method can also be used.
- As this example uses
Abstract
keyword for product, so is it similar to abstract
factory pattern?
No, abstract
word is being used for the sake of defining a product uniquely with essential method, abstract
factory pattern has different set of layers.
- In this example, concrete factory method using composition of methods, so is it similar to
abstract
factory method.
Composition is being used so here just to instantiate single method from UI, in Abstract factory, composition of abstract
method with abstract
keyword is used inside definition of Abstract
factory. For more details, please go through another article on abstract
factory.
References
- https://www.dofactory.com/net/factory-method-design-pattern
Factory
- https://garywoodfine.com/
- https://dev.to/gary_woodfine/simple-factory-pattern-in-c-and-net-core-3263
- https://www.oodesign.com/factory-pattern.html
- https://www.c-sharpcorner.com/UploadFile/akkiraju/factory-design-pattern-vs-factory-method-design-pattern/
- http://shop.oreilly.com/product/9780596007126.do
- https://www.c-sharpcorner.com/UploadFile/akkiraju/factory-design-pattern-vs-factory-method-design-pattern/
- https://vivekcek.wordpress.com/2013/03/17/simple-factory-vs-factory-method-vs-abstract-factory-by-example/
Points of Interest
There are many illustrations on the internet, so please go through a couple of sites to understand where to use it actually.