How To Switch From Factory Method Pattern To Abstract Factory Pattern

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

  1. If you are a novice at abstract factory & factory method pattern.
  2. If you are confused between the abstract factory pattern and factory method pattern.
  3. 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.
  4. If you want to know what each class in the pattern does and how.
  5. 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

  1. MealDeal_AbstractFactory oAbstractFactory_HighProtein = new Mealdeal_ConcreteFactory();  
  2. oAbstractFactory_HighProtein.GetMainMealsOption_AbstractProduct();  
  3. Console.ReadKey();  

Customer Service – Sir, here are the options available under meal deal.

Output

Switch From Factory Method Pattern To Abstract Factory Pattern 

Customer – Thank you very much!

Class Diagram

Switch From Factory Method Pattern To Abstract Factory Pattern

Whole code implementation for Abstract factory pattern.

UIClass.cs

  1. using System;  
  2.   
  3. namespace FactoryMethod_TescoMealDeal  
  4. {  
  5.     class UIClass  
  6.     {  
  7.         static void Main(string[] args)  
  8.         {  
  9.             // Instantiating & Initializing the ConcreteFactory class to get the instance of its parent class AbstractFactory return  
  10.             MealDeal_CreatorFactory oAbstractFactory_HighProtein = new Mealdeal_ConcreteCreatorFactory();  
  11.             oAbstractFactory_HighProtein.GetMainMealsOption_AbstractProduct();  
  12.                          
  13.             Console.ReadKey();  
  14.         }  
  15.     }  
  16.   
  17. }  

Creator.cs

  1. namespace FactoryMethod_TescoMealDeal  
  2. {  
  3.     #region AbstractFactory  
  4.     /// <summary>  
  5.     /// Its abstract class defines factory method  
  6.     /// </summary>     
  7.     abstract class MealDeal_CreatorFactory  
  8.     {  
  9.         public abstract Mealdeal_AbstractProduct GetMainMealsOption_AbstractProduct();  
  10.     }  
  11.  
  12.     #endregion  
  13.  
  14.     #region ConcreteFactory  
  15.     /// <summary>  
  16.     /// The 'Mealdeal_ConcreteCreatorFactory' class :  
  17.     /// Object creation preocess of Concrete products takes place here   
  18.     /// Factory method - implement abstract method to return concrete product  
  19.     /// </summary>  
  20.     class Mealdeal_ConcreteCreatorFactory : MealDeal_CreatorFactory  
  21.     {  
  22.         public override Mealdeal_AbstractProduct GetMainMealsOption_AbstractProduct()  
  23.         {  
  24.             Mealdeal_AbstractProduct meal = new Mealdeal_ConcreteProduct();  
  25.             meal.GetMealdealOption();  
  26.             return meal;  
  27.         }  
  28.     }  
  29.     #endregion  
  30. }  

Product.cs

  1. using System;  
  2.   
  3. namespace FactoryMethod_TescoMealDeal  
  4. {  
  5.     /// <summary>  
  6.     /// Abstract Class/Interface : it defines the method that how the actual product will be created  
  7.     /// </summary>      
  8.     abstract class Mealdeal_AbstractProduct  
  9.     {  
  10.         public abstract void GetMealdealOption();  
  11.     }  
  12.   
  13.     class Mealdeal_ConcreteProduct : Mealdeal_AbstractProduct  
  14.     {  
  15.         public override void GetMealdealOption()  
  16.         {  
  17.             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");  
  18.         }  
  19.     }      
  20. }  

Observations

  1. UI Class instantiates the creator to produce a product.
  2. It produces 1 product

Points to note

  1. 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)
  2. 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.
  3. Where objects are created - Inside concrete creator.
  4. What it produces - A single product.
  5. 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.
  6. 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

  1. 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

  1. SelectMealClient oMealClientHighProtein = new SelectMealClient(oAbstractFactory_HighProtein);  
  2. oMealClientHighProtein.GetMealReady(); 

Client code

  1. class SelectMealClient  
  2.     {  
  3.         MainMeals_AbstractProduct oMain_AbstractProduct;  
  4.         Snacks_AbstractProduct oSnack_AbstractProduct;  
  5.         Drinks_AbstractProduct oDrink_AbstractProduct;  
  6.         public SelectMealClient(MealDeal_AbstractFactory oAbstractFactory)  
  7.         {  
  8. oMain_AbstractProduct = oAbstractFactory.GetMainMealsOption_AbstractProduct();  
  9. oSnack_AbstractProduct = oAbstractFactory.GetSnacksOption_AbstractProduct();  
  10. oDrink_AbstractProduct = oAbstractFactory.GetDrinksOption_AbstractProduct();  
  11.         }  
  12.   
  13.         // Invoking method defined in AbstractProduct implemented in ConcreteProduct  
  14.         public void GetMealReady()  
  15.         {  
  16.             oMain_AbstractProduct.GetMainMealsOption();  
  17.             oSnack_AbstractProduct.GetSnacksOption();  
  18.             oDrink_AbstractProduct.GetDrinksOption();  
  19.         }  
  20. }  

Output

Switch From Factory Method Pattern To Abstract Factory Pattern 

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

  1. MealDeal_AbstractFactory oAbstractFactory_HighFibre = new HighFibre_ConcreteFactory();  
  2. SelectMealClient oMealClientLowcalorie =   
  3. new SelectMealClient(oAbstractFactory_Lowcalorie);  
  4. oMealClientHighFibre.GetMealReady();  

Client code – Same as previous.

Output

Switch From Factory Method Pattern To Abstract Factory Pattern 

Code for Low-calorie diet,

UICode

  1. MealDeal_AbstractFactory oAbstractFactory_Lowcalorie = new LowCalorie_ConcreteFactory();  
  2. SelectMealClient oMealClientHighFibre =   
  3. new SelectMealClient(oAbstractFactory_HighFibre);  
  4.       
  5. oMealClientLowcalorie.GetMealReady();  

Client code – Same as previous.

Output

Switch From Factory Method Pattern To Abstract Factory Pattern 

Points to note
Here, you can see that more options are added, no change in client code is required.

Class Diagram

Switch From Factory Method Pattern To Abstract Factory Pattern

Whole Implementation Code for factory method patterns is mentioned below.

UIClass.cs

  1. using System;  
  2.   
  3. namespace AbstractFactory_TescoMealDeal  
  4. {  
  5.     class UIClass  
  6.     {  
  7.         static void Main(string[] args)  
  8.         {  
  9.             // Instantiating ConcreteFactory class   
  10.             MealDeal_AbstractFactory oAbstractFactory_HighProtein = new HighProtein_ConcreteFactory();  
  11.             MealDeal_AbstractFactory oAbstractFactory_HighFibre = new HighFibre_ConcreteFactory();  
  12.             MealDeal_AbstractFactory oAbstractFactory_Lowcalorie = new LowCalorie_ConcreteFactory();  
  13.   
  14.             // Instantiating & Initializing the Client class using constructor injection  
  15.             // in order to delegate the responsibility of invoking the method(s) defined inside the AbstractFactory  
  16.             SelectMealClient oMealClientHighProtein = new SelectMealClient(oAbstractFactory_HighProtein);  
  17.             SelectMealClient oMealClientHighFibre = new SelectMealClient(oAbstractFactory_HighFibre);  
  18.             SelectMealClient oMealClientLowcalorie = new SelectMealClient(oAbstractFactory_Lowcalorie);  
  19.   
  20.             // Invoke the method to get the actual product using object of Client    
  21.             oMealClientHighProtein.GetMealReady();  
  22.             oMealClientHighFibre.GetMealReady();  
  23.             oMealClientLowcalorie.GetMealReady();  
  24.   
  25.             Console.ReadKey();  
  26.         }  
  27.     }  
  28.  
  29.     #region Client  
  30.     /// <summary>  
  31.     /// This decides how the final product will be prepared.  
  32.     /// </summary>  
  33.     class SelectMealClient  
  34.     {  
  35.         MainMeals_AbstractProduct oMain_AbstractProduct;  
  36.         Snacks_AbstractProduct oSnack_AbstractProduct;  
  37.         Drinks_AbstractProduct oDrink_AbstractProduct;  
  38.   
  39.         // Client Constructor      
  40.         // Note - Client's constructor will use instance of AbstractFactory,    
  41.         // but having hidden information of ConcreteFactory(Child Factory)  
  42.         // this instance (parameter) will invoke the method defined in AbstractFactory,   
  43.         // but implemented in ConcreteFactory returning ConcreteProduct  
  44.         // which (the result of invoked class) will be assigned to the instance of AbstractProduct(Parent), we already declared above under Client  
  45.         public SelectMealClient(MealDeal_AbstractFactory oAbstractFactory)  
  46.         {  
  47.             oMain_AbstractProduct = oAbstractFactory.GetMainMealsOption_AbstractProduct();  
  48.             oSnack_AbstractProduct = oAbstractFactory.GetSnacksOption_AbstractProduct();  
  49.             oDrink_AbstractProduct = oAbstractFactory.GetDrinksOption_AbstractProduct();  
  50.         }  
  51.   
  52.         // Invoking method defined in AbstractProduct implemented in ConcreteProduct  
  53.         public void GetMealReady()  
  54.         {  
  55.             oMain_AbstractProduct.GetMainMealsOption();  
  56.             oSnack_AbstractProduct.GetSnacksOption();  
  57.             oDrink_AbstractProduct.GetDrinksOption();  
  58.         }  
  59.     }  
  60.     #endregion  
  61. }  

Factory.cs

  1. namespace AbstractFactory_TescoMealDeal  
  2. {  
  3.   
  4.     #region AbstractFactory  
  5.     /// <summary>  
  6.     /// Here factory defines the AbstractProduct to be produced (could be in composition; decided by Client)  
  7.     /// but it does not know, what concrete product actually going to be produced.  
  8.     /// </summary>     
  9.     abstract class MealDeal_AbstractFactory  
  10.     {  
  11.         public abstract MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct();  
  12.         public abstract Snacks_AbstractProduct GetSnacksOption_AbstractProduct();  
  13.         public abstract Drinks_AbstractProduct GetDrinksOption_AbstractProduct();  
  14.     }  
  15.  
  16.     #endregion  
  17.  
  18.     #region ConcreteFactory  
  19.     /// <summary>  
  20.     /// Object creation process of Concrete products takes place here   
  21.     /// this knows where to get the concrete product from  
  22.     /// </summary>  
  23.     /// <summary>  
  24.     /// The 'LowCalorie_ConcreteFactory' class  
  25.     /// </summary>  
  26.     class HighProtein_ConcreteFactory : MealDeal_AbstractFactory  
  27.     {  
  28.         public override MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct()  
  29.         {  
  30.             return new HighProteinMain_ConcreteProduct();  
  31.         }  
  32.   
  33.         public override Snacks_AbstractProduct GetSnacksOption_AbstractProduct()  
  34.         {  
  35.             return new HighProteinSnacks_ConcreteProduct();  
  36.         }  
  37.   
  38.         public override Drinks_AbstractProduct GetDrinksOption_AbstractProduct()  
  39.         {  
  40.             return new HighProteinDrinks_ConcreteProduct();  
  41.         }  
  42.     }  
  43.     /// <summary>  
  44.     /// The 'HighFibre_ConcreteFactory' class  
  45.     /// </summary>  
  46.     class HighFibre_ConcreteFactory : MealDeal_AbstractFactory  
  47.     {  
  48.         public override MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct()  
  49.         {  
  50.             return new HighFibreMain_ConcreteProduct();  
  51.         }  
  52.   
  53.         public override Snacks_AbstractProduct GetSnacksOption_AbstractProduct()  
  54.         {  
  55.             return new HighFibreSnacks_ConcreteProduct();  
  56.         }  
  57.   
  58.         public override Drinks_AbstractProduct GetDrinksOption_AbstractProduct()  
  59.         {  
  60.             return new HighFibreDrinks_ConcreteProduct();  
  61.         }  
  62.     }  
  63.   
  64.     /// <summary>  
  65.     /// The 'LowCalorie_ConcreteFactory' class  
  66.     /// </summary>  
  67.     class LowCalorie_ConcreteFactory : MealDeal_AbstractFactory  
  68.     {  
  69.         public override MainMeals_AbstractProduct GetMainMealsOption_AbstractProduct()  
  70.         {  
  71.             return new LowCalorieMain_ConcreteProduct();  
  72.         }  
  73.   
  74.         public override Snacks_AbstractProduct GetSnacksOption_AbstractProduct()  
  75.         {  
  76.             return new LowCalorieSnacks_ConcreteProduct();  
  77.         }  
  78.   
  79.         public override Drinks_AbstractProduct GetDrinksOption_AbstractProduct()  
  80.         {  
  81.             return new LowCalorieDrinks_ConcreteProduct();  
  82.         }  
  83.     }  
  84.     #endregion  
  85. }  

Product.cs

  1. using System;  
  2.   
  3. namespace AbstractFactory_TescoMealDeal  
  4. {  
  5.      
  6.     #region AbstractProduct  
  7.     /// <summary>  
  8.     /// Abstract Class/Interface : it defines the method that how the actual product will be created  
  9.     /// </summary>  
  10.     abstract class MainMeals_AbstractProduct  
  11.     {  
  12.         public abstract void GetMainMealsOption();  
  13.     }  
  14.     abstract class Snacks_AbstractProduct  
  15.     {  
  16.         public abstract void GetSnacksOption();  
  17.     }  
  18.     abstract class Drinks_AbstractProduct  
  19.     {  
  20.         public abstract void GetDrinksOption();  
  21.     }  
  22.  
  23.     #endregion  
  24.  
  25.     #region ConcreteProduct  
  26.     /// <summary>  
  27.     /// The 'Main meal product to be returned from High protein Factory' class  
  28.     /// This is the place where, the functionality of implementating end product is defined.      
  29.     /// </summary>    
  30.  
  31.     #region High Protien  
  32.     class HighProteinMain_ConcreteProduct : MainMeals_AbstractProduct  
  33.     {  
  34.         public override void GetMainMealsOption()  
  35.         {  
  36.             Console.WriteLine("Please select among chicken sandwiches");            
  37.         }  
  38.     }     
  39.     class HighProteinSnacks_ConcreteProduct : Snacks_AbstractProduct  
  40.     {  
  41.         public override void GetSnacksOption()  
  42.         {  
  43.             Console.WriteLine("Please select among Crisps");  
  44.         }  
  45.     }  
  46.     class HighProteinDrinks_ConcreteProduct : Drinks_AbstractProduct  
  47.     {  
  48.         public override void GetDrinksOption()  
  49.         {  
  50.             Console.WriteLine("Please select among Shakes");  
  51.         }  
  52.     }  
  53.     #endregion  
  54.  
  55.     #region High Fibre  
  56.     class HighFibreMain_ConcreteProduct : MainMeals_AbstractProduct  
  57.     {  
  58.         public override void GetMainMealsOption()  
  59.         {  
  60.             Console.WriteLine("Please select among Salads");  
  61.         }  
  62.     }  
  63.     class HighFibreSnacks_ConcreteProduct : Snacks_AbstractProduct  
  64.     {  
  65.         public override void GetSnacksOption()  
  66.         {  
  67.             Console.WriteLine("Please select among BranChocolate");  
  68.         }  
  69.     }  
  70.     class HighFibreDrinks_ConcreteProduct : Drinks_AbstractProduct  
  71.     {  
  72.         public override void GetDrinksOption()  
  73.         {  
  74.             Console.WriteLine("Please select among Smoothies");  
  75.         }  
  76.     }  
  77.     #endregion  
  78.  
  79.     #region Low Calorie  
  80.     class LowCalorieMain_ConcreteProduct : MainMeals_AbstractProduct  
  81.     {  
  82.         public override void GetMainMealsOption()  
  83.         {  
  84.             Console.WriteLine("Please select among veg wraps");  
  85.         }  
  86.     }  
  87.     class LowCalorieSnacks_ConcreteProduct : Snacks_AbstractProduct  
  88.     {  
  89.         public override void GetSnacksOption()  
  90.         {  
  91.             Console.WriteLine("Please select among any fruits");  
  92.         }  
  93.     }  
  94.     class LowCalorieDrinks_ConcreteProduct : Drinks_AbstractProduct  
  95.     {  
  96.         public override void GetDrinksOption()  
  97.         {  
  98.             Console.WriteLine("Please select among Water, caobonated water, lime water");  
  99.         }  
  100.     }  
  101.     #endregion  
  102.  
  103.    #endregion  
  104. }  

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

  1. https://www.dofactory.com/net/factory-method-design-pattern