Abstract Factory Design Pattern in C#

Abstract Factory Design Pattern in C#

In this article, I will discuss the Abstract Factory Design Pattern in C# with an example. The Abstract Factory Design Pattern belongs to the creational patterns and it is one of the most used design patterns in real-world applications.

As part of this article, we are going to discuss the following things.

  1. What is Abstract Factory Design Pattern
  2. Implementation Guidelines of Abstract Factory Pattern
  3. Example to understand the abstract factory pattern
  4. Differences between the Factory Method and Abstract Factory Pattern
What is Abstract Factory Design Pattern?

According to Gang Of Four Definition: “The Abstract Factory Design Pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes” 
We can simplify the above definition as “the Abstract Factory Design Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes”.

The Abstract Factory is a super factory that creates other factories

Implementation Guidelines of Abstract Factory Pattern

We need to Choose the Abstract Factory Design Pattern when 

  1. The application needs to create multiple families of objects or products
  2. We need to use only one of the subset of families of objects at a given point of time
  3. When we want to hide the implementations of the families of products by decoupling the implementation of each of these operations
Let us understand this with an example.

Business Requirement: We need to design an application which will handout computers to Contract and Permanent employees based on the designation and employee type with below specifications

Permanent Employee 

  1. Managerial Position is eligible for Apple Laptop
  2. Non Managerial Position is eligible for Apple desktop

Contract Employee 

  1. Managerial Position is eligible for Dell Laptop
  2. Non Managerial Position is eligible for Dell desktop
Let implement the above requirements step by step:

First, create the necessary database tables to store the employee details. Please below SQL Script to create the Employee Type table with master data and Employee table.

-- Create a database
CREATE DATABASE DesignPatternDB
GO
USE DesignPatternDB
-- Create EmployeeType Table
CREATE TABLE EmployeeType
(
    Id           INT           PRIMARY KEY IDENTITY (1, 1),
    EmployeeType VARCHAR (100) NOT NULL
)
GO
-- Add Permanent and Contract Employees as Master Data 
INSERT INTO EmployeeType VALUES ('Permanet')
INSERT INTO EmployeeType VALUES ('Contract')
GO
-- Create the Employee table
CREATE TABLE Employee
(
    Id					INT          PRIMARY KEY IDENTITY (1, 1),
    Name				VARCHAR (50) NOT NULL,
    JobDescription		VARCHAR (50) NOT NULL,
    Department			VARCHAR (50) NOT NULL,
  ComputerDetails		VARCHAR (250) NULL,
    EmployeeTypeID		INT          REFERENCES EmployeeType(Id)
)
GO
Creating an Empty Console application

Create a console application with the name AbstractFactoryDemo and then add the ADO.NET Entity Data Model against the DesignPatternDB and select the EmployeeType and Employee table that we just created in our previous step.

Once you add the above two tables, your EDMX file should look as shown below.

Abstract Factory Design Pattern in C#

Step1: Creating Enums

Add a Folder with the name Enum at the root level of the application. Here we need to create some enumerations related to our business requirements

To do so add a class file with the name Enumerations inside the Enum Folder and then copy and paste the following code. The code is self-explained, please go through the comments.

namespace AbstractFactoryDemo.Enums
{
    // As we are going to use Laptop and Desktop computer types, 
    // so create an enum which contains these two computer types
    public enum ComputerTypes
    {
        Laptop,
        Desktop
    }

    // As we know the brands for the Laptop and Desktop will be APPLE and DELL,  
    // so create another enum which contains the APPLE and DELL Brands
    public enum Brands
    {
        APPLE,
        DELL
    }

    // Finally, let's create another enum which will contains the processors 
    // such as I3, I5 and I7
    public enum Processors
    {
        I3,
        I5,
        I7
    }
}
Step2: Creating abstract product interfaces

Now we need to create the abstract product interfaces which will declare the type of product. Here we need to declare three interfaces such as IBrand, IProcessor and ISystemType and these three abstract product interfaces compose a product which is in our case may be a laptop or desktop.

To do so, first, add a Folder with the name AbstractProduct within the AbstractFactory folder.

Then create three interfaces with the name IBrand, IProcessor and ISystemType within the AbstractProduct Folder as shown below.

namespace AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct
{
    public interface IBrand
    {
        string GetBrand();
    }
}

namespace AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct
{
    public interface IProcessor
    {
        string GetProcessor();
    }
}

namespace AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct
{
    public interface ISystemType
    {
        string GetSystemType();
    }
}
Step3: Creating Concrete Products

In step2, we have created three interfaces (IBrand, IProcessor and ISystemType), now we need to implement the above interfaces.

To do so, first create a folder with the name ConcreteProduct within the AbstractFactory Folder. Next, add a class file with the name Brands within the ConcreteProduct Folder and then copy and paste the following code.

namespace AbstractFactoryDemo.Factory.AbstractFactory.ConcreteProduct
{ 
    public class APPLE : IBrand
    {
        public string GetBrand()
        {
            return Enums.Brands.APPLE.ToString();
        }
    }

    public class DELL : IBrand
    {
        public string GetBrand()
        {
            return Enums.Brands.DELL.ToString();
        }
    }
}

As you can see we have created two classes with the name APPLE and DELL and both the classes are implement the IBrand interface. These two APPLE and DELL classes can be treated as Family of Brand classes.

Now we need to implement the IProcessor interface. To do so create a new class file with the name Processor within the ConcreteProduct Folder and then copy and paste the following code.

namespace AbstractFactoryDemo.Factory.AbstractFactory.ConcreteProduct
{ 
    public class I7 : IProcessor
    {
        public string GetProcessor()
        {
            return Processors.I7.ToString();
        }
    }
    public class I5 : IProcessor
    {
        public string GetProcessor()
        {
            return Processors.I5.ToString();
        }
    }
}

Here we also created a family of related Processor classes such as I5 and I7.

In the same way we need to implement the ISystemType interface. To do so create a class file with the name ComputerSystemTypes within the ConcreteProduct Folder and then copy and paste the following code.

namespace AbstractFactoryDemo.Factory.AbstractFactory.ConcreteProduct
{ 
    public class Laptop : ISystemType
    {
        public string GetSystemType()
        {
            return ComputerTypes.Laptop.ToString();
        }
    }
    public class Desktop : ISystemType
    {
        public string GetSystemType()
        {
            return ComputerTypes.Desktop.ToString();
        }
    }
}

Here we also created a family of related classes like Laptop and Desktop. Now as we have a family of related products such as Brands, Processors, and System types, let’s create an interface which will compose all these products/classes.

Step4: Creating Abstract Interface

Here we need to create an abstract interface which will compose all the family of related products. So first create a Folder with the name AbstractInterface within the AbstractFactory folder.

Next create a class file with the name IComputerFactory within the AbstractInterface folder and then copy and paste the following code.

using AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct;
namespace AbstractFactoryDemo.Factory.AbstractFactory.AbstractInterface
{ 
    public interface IComputerFactory
    {
        IProcessor Processor();
        IBrand Brand();
        ISystemType SystemType();
    }
}

Once you create the above abstract interface next you need to create the concrete factories which will implement the above abstract interface.

Step5: Creating Concrete Factory

Here we need to create the concrete factories by implementing the abstract interface IComputerFactory. As we have as of now two types of systems such as APPLE and DELL, let’s first create the Concrete factory for the APPLE system.

First, create a Folder with the name ConcreteFactory within the AbstractFactory Folder. And then add a class file with the name AppleFactory within the ConcreteFactory folder and then copy and paste the following code.

The code is self-explained, so please go through the comments.

using AbstractFactoryDemo.Factory.AbstractFactory.AbstractInterface;
using AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct;
using AbstractFactoryDemo.Factory.AbstractFactory.ConcreteProduct;

namespace AbstractFactoryDemo.Factory.AbstractFactory.ConcreteFactory
{
    // implementing the abstract factory interface IComputerFactory
    // to create concrete product for APPLE Laptop and Desktop
    public class AppleFactory : IComputerFactory
    {
        public IBrand Brand()
        {
            return new APPLE();
        }

        public IProcessor Processor()
        {
            return new I7();
        }

        public virtual ISystemType SystemType()
        {
            return new Desktop();
        }
    }

    // AS for Laptop and Desktop the Brand and Peocessor Remains the same
    // So, lets implement the MACFactory to create the MACLaptopFactory product
    // and here we need to override the SystemType function
    public class AppleLaptopFactory : AppleFactory
    {
        public override ISystemType SystemType()
        {
            return new Laptop();
        }
    }
}

In the same way, we need to create the DELL Factory product for both Laptop and Desktop. To do so, create a class File with the name DellFactory within the ConcreteFactory folder and then copy and paste the following code.

using AbstractFactoryDemo.Factory.AbstractFactory.AbstractInterface;
using AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct;
using AbstractFactoryDemo.Factory.AbstractFactory.ConcreteProduct;

namespace AbstractFactoryDemo.Factory.AbstractFactory.ConcreteFactory
{
    public class DellFactory : IComputerFactory
    {
        public IBrand Brand()
        {
            return new DELL();
        }

        public IProcessor Processor()
        {
            return new I5();
        }

        public virtual ISystemType SystemType()
        {
            return new Desktop();
        }
    }
    public class DellLaptopFactory : DellFactory
    {
        public override ISystemType SystemType()
        {
            return new Laptop();
        }
    }
}
Creating the Abstract Factory:

Once you have created the Apple Factory and Dell factory, now we need to create another factory which will create the appropriate Factory object either Apple factory object or Dell Factory Object.

Here we need to implement the business requirement. Based on the Employee Type and Job Description, the create method will create the appropriate factory object and return that object through IComputerFactory, the IComputerFactory contains the Processor, Brand and system type information

So create a class File with the name EmployeeSystemFactory within the ConcreteFactory folder and then copy and paste the following code.

using AbstractFactoryDemo.Factory.AbstractFactory.AbstractInterface;

namespace AbstractFactoryDemo.Factory.AbstractFactory.ConcreteFactory
{
    public class EmployeeSystemFactory
    {
        public IComputerFactory Create(Employee e)
        {
            IComputerFactory returnValue = null;
            if (e.EmployeeTypeID == 1)
            {
                if (e.JobDescription == "Manager")
                {
                    returnValue = new AppleLaptopFactory();
                }
                else
                {
                    returnValue = new AppleFactory();
                }
            }
            else if (e.EmployeeTypeID == 2)
            {
                if (e.JobDescription == "Manager")
                {
                    returnValue = new DellLaptopFactory();
                }
                else
                    returnValue = new DellFactory();
            }
            return returnValue;
        }
    }
}
Step6: Creating the Client

First, create a Folder with the name Client within the AbstractFactory Folder. Here we need to create a class which will return the system details.

So create a class file with the name EmployeeSystemManager within the Client Folder and then copy and paste the following code.

using AbstractFactoryDemo.Factory.AbstractFactory.AbstractInterface;
using AbstractFactoryDemo.Factory.AbstractFactory.AbstractProduct;
namespace AbstractFactoryDemo.Factory.AbstractFactory.Client
{
    public class EmployeeSystemManager
    {
        IComputerFactory _IComputerFactory;
        public EmployeeSystemManager(IComputerFactory iComputerFactory)
        {
            _IComputerFactory = iComputerFactory;
        }
        public string GetSystemDetails()
        {
            IBrand brand = _IComputerFactory.Brand();
            IProcessor processor = _IComputerFactory.Processor();
            ISystemType systemType = _IComputerFactory.SystemType();
            string returnValue = string.Format("{0} {1} {2}", brand.GetBrand(),
                systemType.GetSystemType(), processor.GetProcessor());
            return returnValue;
        }
    }
}

So that’s it. We have done with our implementation. The complete folder structure of our application should looks as shown below.

Abstract Factory Design Pattern in C#

Testing the application:

So let’s modify the Main Method of the Program class as shown below to test whether everything is working as expected or not.

using AbstractFactoryDemo.Factory.AbstractFactory.AbstractInterface;
using AbstractFactoryDemo.Factory.AbstractFactory.Client;
using AbstractFactoryDemo.Factory.AbstractFactory.ConcreteFactory;
using System;

namespace AbstractFactoryDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create the Employee Object
            Employee employee = new Employee()
            {
                Name = "Pranaya",
                JobDescription = "Manager",
                Department = "HR",
                EmployeeTypeID = 1
            };
            
            // Create the abstract factory object
            IComputerFactory factory = new EmployeeSystemFactory().Create(employee);

            // use the abstract factory object while creating the client and 
            // then get the system details
            EmployeeSystemManager manager = new EmployeeSystemManager(factory);
            employee.ComputerDetails = manager.GetSystemDetails();

            // Store the Employee into the database
            using (DesignPatternDBEntities dBEntities = new DesignPatternDBEntities())
            {
                dBEntities.Employees.Add(employee);
                dBEntities.SaveChanges();
            }

            Console.WriteLine("Employee data inserted");
            Console.ReadKey();
        }
    }
}

So now run the application and see everything is working as expected. The data gets inserted into the Employee table as expected.

Let’s understand the Flow of Abstract Design Pattern

Abstract Factory Design Pattern in C#

  1. The Client (EmployeeSystemManager class) uses the AbstractFactory (i.e. IComputerFactory interface) and AbstractProduct interfaces (IBrand, IProcessor, and ISystemType) to create a family of related objects.
  2. AbstractFactory (IComputerFactory) is an interface which is used to create abstract product (Processor(), Brand() and SystemType() abstract methods).
  3. The ConcreteFactory (AppleFactory or DellFactory) is a class which implements the AbstractFactory (IComputerFactory) interface to create concrete products.
  4. AbstractProduct (IBrand, IProcessor or ISystemType) is an interface which declares a type of product.
  5. The ConcreteProduct (Brand, Processor or ComputerSystemTypes) is a class which implements the AbstractProduct (IBrand, IProcessor or ISystemType) interface to create the product.
Differences between Abstract Factory and Abstract Method Pattern:
  1. Abstract Factory Design Pattern adds a layer of abstraction to the Factory Method Design Pattern
  2. The Abstract Factory design pattern implementation can have multiple factory methods
  3. Similar products of a factory implementation are grouped in Abstract factory
  4. The Abstract Factory Pattern uses object composition to decouple applications form specific implementations
  5. The Factory Method Pattern uses inheritance to decouple applications form specific implementations
SUMMARY

In this article, I try to explain the Abstract Factory Design Pattern in C# step by step with an example. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

Leave a Reply

Your email address will not be published. Required fields are marked *