Inspiration
While going through these 2 patterns (Factory Method Pattern and Abstract Factory Pattern), I always wondered they have a similarity in name; should there be some similarity in their code as well. However, I never got any single case study which could be used for both the patterns. Actually, they both work at different levels and can’t be compared. I always wanted to use a single case study for illustrating both the patterns in the interest of understanding where they can or can’t be used.
Introduction
These are the patterns that fall under "Creation design patterns" category defined by “Gang of Four.”
- Abstract Factory pattern
Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
- Factory Method Pattern
Defines an interface for creating an object, but lets the subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Who should read this article
- If you are a novice at abstract factory & factory method pattern.
- If you are confused between the abstract factory pattern and factory method pattern.
- If you have gone through many examples related to the abstract factory and factory method, but still unable to bridge the gap between these two.
- If you want to know what each class in the pattern does and how.
- If you want to spot the exact place where changes happen while switching from factory to abstract factory.
About the Article
The actual programming scenario case may not be the same for both the patterns, but for making these complicated design patterns, I am taking a simple real-life example of a pack of the meal offered by a food store Tesco Express. I am using the same case study example so that the difference can be identified between these two design patterns.
This article will also highlight the differences between Abstract Factory and Factory method pattern.
A Real-world example – Meal Deal offered by Tesco Express
Factory Method Pattern
A real-world scenario,
Sometimes, you have seen food stores like Tesco Express offering meal deal where you can select 1 type of the main course (Sandwich/salad/wraps), 1 type of snacks (Crisp/Chocolate/Fruit), and 1 type of drink (shake, smoothie, water).
In Programming Terms
Delegating a responsibility of production to a specialist, with your specifications. Comparing design pattern terms with this real-world example, we get the following observations.
- You – Main method
- Tesco Express – The Creator or Interface
- Customer service – The Concrete creator
- A definition of Meal deal – Abstract Product
- A pack of Meal deal – Concrete Product
Real-life conversation along with the implementation of the code with the factory method
A customer goes to Tesco Express and converses with customer service
- Customer– Hi, do you have any offer for the meal?
- Customer Service – Yes, why don’t you try our meal deal option? Here you can have 1 main meal, 1 snack, and 1 drink.
- Customer –Sure, what are the option available with it?
(Action - Customer Service checks for the options available.)
UI Code
- MealDeal_AbstractFactory oAbstractFactory_HighProtein = new Mealdeal_ConcreteFactory();
- oAbstractFactory_HighProtein.GetMainMealsOption_AbstractProduct();
- Console.ReadKey();
Customer Service – Sir, here are the options available under meal deal.
Output
Customer – Thank you very much!
Class Diagram
Whole code implementation for Abstract factory pattern.
UIClass.cs
- using System;
-
- namespace FactoryMethod_TescoMealDeal
- {
- class UIClass
- {
- static void Main(string[] args)
- {
-
- MealDeal_CreatorFactory oAbstractFactory_HighProtein = new Mealdeal_ConcreteCreatorFactory();
- oAbstractFactory_HighProtein.GetMainMealsOption_AbstractProduct();
-
- Console.ReadKey();
- }
- }
-
- }
Creator.cs
- namespace FactoryMethod_TescoMealDeal
- {
- #region AbstractFactory
-
-
-
- abstract class MealDeal_CreatorFactory
- {
- public abstract Mealdeal_AbstractProduct GetMainMealsOption_AbstractProduct();
- }
-
- #endregion
-
- #region ConcreteFactory
-
-
-
-
-
- class Mealdeal_ConcreteCreatorFactory : MealDeal_CreatorFactory
- {
- public override Mealdeal_AbstractProduct GetMainMealsOption_AbstractProduct()
- {
- Mealdeal_AbstractProduct meal = new Mealdeal_ConcreteProduct();
- meal.GetMealdealOption();
- return meal;
- }
- }
- #endregion
- }
Product.cs
- using System;
-
- namespace FactoryMethod_TescoMealDeal
- {
-
-
-
- abstract class Mealdeal_AbstractProduct
- {
- public abstract void GetMealdealOption();
- }
-
- class Mealdeal_ConcreteProduct : Mealdeal_AbstractProduct
- {
- public override void GetMealdealOption()
- {
- Console.WriteLine("Please select any one item from each of :\n sandwiches/salads/veg wraps as main meal \n and crisp/branchcolate/fruit as snacks \n and shakes/Smoothei/Water as drink");
- }
- }
- }
Observations
- UI Class instantiates the creator to produce a product.
- It produces 1 product
Points to note
- Where is it used – When the responsibility of an object creation to be delegated to a factory i.e., I want a meal deal (having the main meal, snack, & drink)
- How objects are created - The object of the product is being created using inheritance of creator method which defines the abstract product, as it says instantiation to be deferred to the subclass which is creator class here.
- Where objects are created - Inside concrete creator.
- What it produces - A single product.
- The caller of the factory – Here caller of the factory is the main class, who knows which concrete factory will be used to create a product.
- Creator/Factory – It just defines a method to produce a product but it does not know which child product is being produced.
Abstract Factory
Real world scenario
There are many options available with the meal deal. It is a bit hard to choose among each of three types. So, how could it be improved? What if someone likes all his meal deal items with full of protein or fiber or low-calorie. The solution to classify these meals among specific categories like high protein or fiber can be implemented by using the abstract factory pattern.
In Programming Terms
Provide an Interface/Abstract Class (of Product/Factory) for creating families of related or dependent objects without specifying their concrete classes (ConcreateProduct/ConcreteFactory).
Comparing design pattern terms with a real-world example.
- Customer – Main method
- Tesco Express/Customer service - Client
- Meal pack (combination of one MainMeal + snacks + drink) - abstract factory
- Meal pack of high protein, low-calorie, high fiber meal – factory
- MainMeals, Snacks, Drink – Abstract product
- (High-protein Main, High-fibre snacks, Low-calorie Drinks etc.) – Concrete Product
Real life conversation along with abstract factory code will go like this.
Customer goes to Tesco Express and converses with the customer service,
- Customer– Hi, do you have any offer for a meal?
- Customer Service – Yes, why don’t you try our meal deal option? Here, you can have 1 main meal, 1 snack, and 1 drink.
- Customer –Sure, what are the option available with it?
- Action - Customer Service checks for the options available
UI Code
- MealDeal_AbstractFactory oAbstractFactory_HighProtein =new HighProtein_ConcreteFactory();
- Customer Service – What types of food you would like to have in your meal? We have 3 types of diet, high protein, high fiber, low-calorie.
- Customer – Thanks! I would like to go have the high protein in my meal, what items I can choose for this?
Customer service checks for the deal options for high-protein diet.
UICode
- SelectMealClient oMealClientHighProtein = new SelectMealClient(oAbstractFactory_HighProtein);
- oMealClientHighProtein.GetMealReady();
Client code
- class SelectMealClient
- {
- MainMeals_AbstractProduct oMain_AbstractProduct;
- Snacks_AbstractProduct oSnack_AbstractProduct;
- Drinks_AbstractProduct oDrink_AbstractProduct;
- public SelectMealClient(MealDeal_AbstractFactory oAbstractFactory)
- {
- oMain_AbstractProduct = oAbstractFactory.GetMainMealsOption_AbstractProduct();
- oSnack_AbstractProduct = oAbstractFactory.GetSnacksOption_AbstractProduct();
- oDrink_AbstractProduct = oAbstractFactory.GetDrinksOption_AbstractProduct();
- }
-
-
- public void GetMealReady()
- {
- oMain_AbstractProduct.GetMainMealsOption();
- oSnack_AbstractProduct.GetSnacksOption();
- oDrink_AbstractProduct.GetDrinksOption();
- }
- }
Output
Customer Service – You can have among sandwiches, crisps, and shakes.
Customer – Nice, may I know what I need to select if I go with High-fibre and Low-calorie diet.
Code for High-fibre diet,
UICode
- MealDeal_AbstractFactory oAbstractFactory_HighFibre = new HighFibre_ConcreteFactory();
- SelectMealClient oMealClientLowcalorie =
- new SelectMealClient(oAbstractFactory_Lowcalorie);
- oMealClientHighFibre.GetMealReady();
Client code – Same as previous.
Output
Code for Low-calorie diet,
UICode
- MealDeal_AbstractFactory oAbstractFactory_Lowcalorie = new LowCalorie_ConcreteFactory();
- SelectMealClient oMealClientHighFibre =
- new SelectMealClient(oAbstractFactory_HighFibre);
-
- oMealClientLowcalorie.GetMealReady();
Client code – Same as previous.
Output
Points to note
Here, you can see that more options are added, no change in client code is required.
Class Diagram
Whole Implementation Code for factory method patterns is mentioned below.
UIClass.cs
- using System;
-
- namespace AbstractFactory_TescoMealDeal
- {
- class UIClass
- {
- static void Main(string[] args)
- {
-
- MealDeal_AbstractFactory oAbstractFactory_HighProtein = new HighProtein_ConcreteFactory();
- MealDeal_AbstractFactory oAbstractFactory_HighFibre = new HighFibre_ConcreteFactory();
- MealDeal_AbstractFactory oAbstractFactory_Lowcalorie = new LowCalorie_ConcreteFactory();
-
-
-
- SelectMealClient oMealClientHighProtein = new SelectMealClient(oAbstractFactory_HighProtein);
- SelectMealClient oMealClientHighFibre = new SelectMealClient(oAbstractFactory_HighFibre);
- SelectMealClient oMealClientLowcalorie = new SelectMealClient(oAbstractFactory_Lowcalorie);
-
-
- oMealClientHighProtein.GetMealReady();
- oMealClientHighFibre.GetMealReady();
- oMealClientLowcalorie.GetMealReady();
-
- Console.ReadKey();
- }
- }
-
- #region Client
-
-
-
- class SelectMealClient
- {
- MainMeals_AbstractProduct oMain_AbstractProduct;
- Snacks_AbstractProduct oSnack_AbstractProduct;
- Drinks_AbstractProduct oDrink_AbstractProduct;
-
-
-
-
-
-
-
- public SelectMealClient(MealDeal_AbstractFactory oAbstractFactory)
- {
- oMain_AbstractProduct = oAbstractFactory.GetMainMealsOption_AbstractProduct();
- oSnack_AbstractProduct = oAbstractFactory.GetSnacksOption_AbstractProduct();
- oDrink_AbstractProduct = oAbstractFactory.GetDrinksOption_AbstractProduct();
- }
-
-
- public void GetMealReady()
- {
- oMain_AbstractProduct.GetMainMealsOption();
- oSnack_AbstractProduct.GetSnacksOption();
- oDrink_AbstractProduct.GetDrinksOption();
- }
- }
- #endregion
- }
Factory.cs
- namespace AbstractFactory_TescoMealDeal
- {
-
- #region AbstractFactory
-
-
-
-
- abstract class MealDeal_AbstractFactory
- {
- public abstract MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct();
- public abstract Snacks_AbstractProduct GetSnacksOption_AbstractProduct();
- public abstract Drinks_AbstractProduct GetDrinksOption_AbstractProduct();
- }
-
- #endregion
-
- #region ConcreteFactory
-
-
-
-
-
-
-
- class HighProtein_ConcreteFactory : MealDeal_AbstractFactory
- {
- public override MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct()
- {
- return new HighProteinMain_ConcreteProduct();
- }
-
- public override Snacks_AbstractProduct GetSnacksOption_AbstractProduct()
- {
- return new HighProteinSnacks_ConcreteProduct();
- }
-
- public override Drinks_AbstractProduct GetDrinksOption_AbstractProduct()
- {
- return new HighProteinDrinks_ConcreteProduct();
- }
- }
-
-
-
- class HighFibre_ConcreteFactory : MealDeal_AbstractFactory
- {
- public override MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct()
- {
- return new HighFibreMain_ConcreteProduct();
- }
-
- public override Snacks_AbstractProduct GetSnacksOption_AbstractProduct()
- {
- return new HighFibreSnacks_ConcreteProduct();
- }
-
- public override Drinks_AbstractProduct GetDrinksOption_AbstractProduct()
- {
- return new HighFibreDrinks_ConcreteProduct();
- }
- }
-
-
-
-
- class LowCalorie_ConcreteFactory : MealDeal_AbstractFactory
- {
- public override MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct()
- {
- return new LowCalorieMain_ConcreteProduct();
- }
-
- public override Snacks_AbstractProduct GetSnacksOption_AbstractProduct()
- {
- return new LowCalorieSnacks_ConcreteProduct();
- }
-
- public override Drinks_AbstractProduct GetDrinksOption_AbstractProduct()
- {
- return new LowCalorieDrinks_ConcreteProduct();
- }
- }
- #endregion
- }
Product.cs
Observation on Abstract Factory
- Abstract Product
- 1 abstract product is limited to 1 type of object implemented by a concrete product.
- Abstract factory
- Abstract factory is responsible to make complete product includes family or composition of abstract products.
- Client
- No changes take place in client code
- The client just receives the instance of the abstract factory (holds the reference of child/concrete factory) from main method (i.e. Customer says high fiber diet)
- Concrete factory
- Concrete factory knows which concrete product to be returned.
Points to Note
- Where is it used - Abstract factory is used to produce a composed product (or family of related products) based on 2 or more filters (i.e. I want a meal deal (having main meal, snack & drink) with high protein) or you can say “applying another level of abstraction over a factory method”.
- How an object is created - Objects are created using composition.
- Where objects are created - Concrete factories implement factory method to create a product.
- What it produces - It produces a family of a related object.
- The caller of a factory – Caller of a factory, (which is called client here) does not know which concrete factory will be used to create a product.
- Factory – It holds a method to produce a composition of products or its family but it does not even know which concrete factory will be used to create a specific product.
References
- https://www.dofactory.com/net/factory-method-design-pattern