Design Pattern Tutorials

Abstract Factory Design Pattern

In this tutorial we will learn

  • What is Abstract Factory Design Pattern
  • Implementation Guidelines
  • Abstract factory implementation
  • Differences between Factory Method and Abstract Factory Pattern

What is Abstract Factory Design Pattern?

Gang Of Four Definition : "The Abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes"

The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes

Abstract Factory pattern belongs to creational patterns and is one of the most used design patterns in real world applications Abstract factory is a super factory that creates other factories

Implementation Guidelines

We need to Choose Abstract Factory Pattern when :
  • The application need to create multiple families of objects or products
  • We need to use only one of the subset of families of objects at a given point of time
  • We want to hide the implementations of the families of products by decoupling the implementation of each of these operations

Business Requirement : Handout computers to Contract and Permanent employees based on the designation and employee type with below specifications
Permanent Employee
a) Managerial Position is eligible for Apple MAC Book Laptop
b) Non Managerial Position is eligible for Apple IMac desktop
Contract Employee
a) Managerial Position is eligible for Dell Laptop
b) Non Managerial Position is eligible for Dell desktop

Abstract Factory Representation

Abstract Factory Representation
If you look at the illustrated diagram of Abstract Factory Representation :
  • Client is a class which use AbstractFactory and AbstractProduct interfaces to create a family of related objects.
  • AbstractFactory is an interface which is used to create abstract product.
  • ConcreteFactory is a class which implements the AbstractFactory interface to create concrete products.
  • AbstractProduct is an interface which declares a type of product.
  • ConcreteProduct is a class which implements the AbstractProduct interface to create product.

Steps to solve the above business requirement

Step 1 : Add ComputerDetails to the existing Employee table

CREATE TABLE [dbo].[Employee]
(
    [Id]               INT IDENTITY (1, 1) NOT NULL,
    [Name]             VARCHAR (50) NOT NULL,
    [JobDescription]   VARCHAR (50) NOT NULL,
    [Number]           VARCHAR (50) NOT NULL,
    [Department]       VARCHAR (50) NOT NULL,
    [HourlyPay]        DECIMAL (18) NOT NULL,
    [Bonus]            DECIMAL (18) NOT NULL,
    [EmployeeTypeID]   INT          NOT NULL,
    [HouseAllowance]   DECIMAL (18) NULL,
    [MedicalAllowance] DECIMAL (18) NULL,
    [ComputerDetails]  VARCHAR (250) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_Employee_EmployeeType] FOREIGN KEY ([EmployeeTypeID])
     REFERENCES [dbo].[Employee_Type] ([Id])

)

Step 2 : Open EmployeePortal.edmx under the Models folder of the solution and update the model from the database (Right click on the model designer and choose update from database option)
Step 3 : Create AbstractFactory folder under existing Factory folder and add the below classes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Web.Factory.AbstractFactory
{
    public interface IBrand
    {
        string GetBrand();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Web.Factory.AbstractFactory
{
    public interface ISystemType
    {
        string GetSystemType();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using static Web.Factory.AbstractFactory.Enumerations;

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.Factory.AbstractFactory
{
    public class DELL : IBrand
    {
        public string GetBrand()
        {
            return Enumerations.Brands.DELL.ToString();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using static Web.Factory.AbstractFactory.Enumerations;

namespace Web.Factory.AbstractFactory
{
    public class MAC : IBrand
    {
        public string GetBrand()
        {
            return Brands.APPLE.ToString();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using static Web.Factory.AbstractFactory.Enumerations;

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.Factory.AbstractFactory
{
    public class Enumerations
    {
        public enum ComputerTypes
        {
            Laptop,
            Desktop
        }
        public enum Brands
        {
            APPLE,
            DELL
        }
        public enum Processors
        {
            I3,
            I5,
            I7
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Web.Factory.AbstractFactory
{
    public interface IComputerFactory
    {
        IProcessor Processor();
        IBrand Brand();
        ISystemType SystemType();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.Factory.AbstractFactory
{
    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();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Web.Models;

namespace Web.Factory.AbstractFactory
{
    public class EmployeeSystemFactory
    {
        public IComputerFactory Create(Employee e)
        {
            IComputerFactory returnValue = null;
            if(e.EmployeeTypeID ==1)
            {
                if(e.JobDescription == "Manager")
                {
                    returnValue = new MACLaptopFactory();
                }
                else
                {
                    returnValue = new MACFactory();
                }
            }
            else if (e.EmployeeTypeID == 2)
            {
                if (e.JobDescription == "Manager")
                {
                    returnValue = new DellLaptopFactory();
                }
                else
                    returnValue = new DellFactory();
            }
            return returnValue;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.Factory.AbstractFactory
{
    public class MACFactory : IComputerFactory
    {
        public IBrand Brand()
        {
            return new MAC();
        }

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

        public virtual ISystemType SystemType()
        {
            return new Desktop();
        }
    }
    public class MACLaptopFactory : MACFactory
    {
      
        public override ISystemType SystemType()
        {
            return new Laptop();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web.Factory.AbstractFactory
{
    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;
        }
    }
}


Step 4 : Now, integrate the AbstractFactory and Client in the EmployeesController’s Create method and replace the existing logic as stated below.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,JobDescription,Number,Department,HourlyPay,Bonus,EmployeeTypeID")] Employee employee)
{
    if (ModelState.IsValid)
    {
        BaseEmployeeFactory empFactory = new EmployeeManagerFactory().CreateFactory(employee);
        empFactory.ApplySalary();
        IComputerFactory factory = new EmployeeSystemFactory().Create(employee);
        EmployeeSystemManager manager = new EmployeeSystemManager(factory);
        employee.ComputerDetails = manager.GetSystemDetails();
        db.Employees.Add(employee);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    ViewBag.EmployeeTypeID = new
        SelectList(db.Employee_Type, "Id", "EmployeeType",
        employee.EmployeeTypeID);
    return View(employee);
}


Step 5 : Arrange the classes in the AbstractFactory folder with the below structure.
Abstract Factory Representation
Step 6 : Enhance the current Employee index view to add ComputerDetails columns.
Step 7 : Run the application and notice that we have achieved the business requirement by using Abstract Factory pattern.
Abstract Factory Representation
Step 8 : Abstract Factory and Factory Method
  • Abstract factory pattern adds a layer of abstraction to the factory method pattern
  • Abstract factory pattern implementation can have multiple factory methods
  • Similar products of a factory implementation are grouped in Abstract factory
  • Abstract Factory uses object composition to decouple applications form specific implementations
  • Factory Method uses inheritance to decouple applications form specific implementations

;