Sunday, 8 July 2018

Abstract Factory Design Pattern

  • Delegate, you do it on my behalf
  • Create related or dependent objects without specifying there concrete classes
  • Is a super factory that creates other factories
  • Adds a layer of abstraction to the factory method pattern
 Following example will try to illustrate Abstract Design Pattern

Handout computers to Contract and Permanent employees based on the designation and employee type with below specifications:

Permanent employee
  • Managerial position is eligible for mac book laptop
  • Non managerial position is eligible for iMac desktop
Contract employee
  • Managerial position is eligible for dell laptop
  • Non managerial position is eligible for dell desktop

Sample Code Download: PatternsAbstractMethod.zip (Visual Studio 2017 Console App)


Basic Setup

 

Enums


     public enum ComputerBrand
    {
        Apple,
        Dell
    }

    public enum ComputerProcessor
    {
        I3,
        I5,
        I7
    }

    public enum ComputerType
    {
        Laptop,
        Desktop
    }

    public enum EmployeeDesignation
    {
        Manager,
        NonManager
    }

    public enum EmployeeType
    {
        Contract = 1,
        Permanent = 2
    }

Model to Represent DB


    public class EmpData
    {
        public string Name { get; set; }
        public EmployeeDesignation Designation { get; set; }
        public string Department { get; set; }
        public EmployeeType EmployeeType { get; set; }
        public string ComputerDetails { get; set; }
    }


Filling and Displaying the Data


    class Program
    {
        static void Main(string[] args)
        {

            var employees = FillEmployees();

            PrintEmployees(employees);

            Console.ReadLine();
        }

        private static List FillEmployees()
        {
            var employees = new List
            {
                new EmpData
                {
                    Name = "Jason",
                    Department = "IT",
                    Designation = EmployeeDesignation.Manager,
                    EmployeeType = EmployeeType.Contract
                },
                new EmpData
                {
                    Name = "Jay",
                    Department = "IT",
                    Designation = EmployeeDesignation.NonManager,
                    EmployeeType = EmployeeType.Contract
                },
                new EmpData
                {
                    Name = "Brian",
                    Department = "IT",
                    Designation = EmployeeDesignation.Manager,
                    EmployeeType = EmployeeType.Permanent
                },
                new EmpData
                {
                    Name = "Susan",
                    Department = "IT",
                    Designation = EmployeeDesignation.NonManager,
                    EmployeeType = EmployeeType.Permanent
                }
            };

            return employees;
        }

        private static string Tab()
        {
            return "\t";
        }

        private static void PrintEmployees(List employees)
        {
            //header
            Console.ForegroundColor = ConsoleColor.DarkYellow;
            var header = $"#{Tab()}Name{Tab()}Department{Tab()}Type{Tab()}{Tab()}Designation{Tab()}ComputerDetails";
            Console.WriteLine(header);
            Console.ResetColor();

            //no data
            if (employees == null || !employees.Any())
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("No Data");
                Console.ResetColor();
                return;
            }

            //print data
            var row = 1;
            foreach (var emp in employees)
            {
                var message = row + Tab() + emp.Name + Tab() + emp.Department + Tab()+Tab() + emp.EmployeeType+Tab()+emp.Designation + Tab();
                message += !string.IsNullOrWhiteSpace(emp.ComputerDetails)
                    ? emp.ComputerDetails + Tab()
                    : "" + Tab();
                Console.WriteLine(message);
                row += 1;
            }

        }
    }







Implementation


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




Adding Abstract Product Interfaces


Under folder AbstractProuct, create following interfaces


    public interface ISystemBrand
    {
        //computer brand
        string GetBrand();
    }

    public interface ISystemProcessor
    {
        string GetProcessor();
    }

    public interface ISystemType
    {
        string GetSystemType();
    }



Adding Concrete Product Classes


Here we'll add the product classes which will implement the AbstractProduct Interfaces. Under folder ConcreteProduct add the following classes by implementing appropriate interfaces.

    public class SystemMac : ISystemBrand
    {
        public string GetBrand()
        {
            return ComputerBrand.Apple.ToString();
        }
    }

    public class SystemDell : ISystemBrand
    {
        public string GetBrand()
        {
            return ComputerBrand.Dell.ToString();
        }
    }

    public class SystemTypeDesktop : ISystemType
    {
        public string GetSystemType()
        {
            return ComputerType.Desktop.ToString();
        }
    }

    public class SystemTypeLaptop : ISystemType
    {
        public string GetSystemType()
        {
            return ComputerType.Laptop.ToString();
        }
    }

    public class ProcessorI3 : ISystemProcessor
    {
        public string GetProcessor()
        {
            return ComputerProcessor.I3.ToString();
        }
    }

    public class ProcessorI5 : ISystemProcessor
    {
        public string GetProcessor()
        {
            return ComputerProcessor.I5.ToString();
        }
    }

    public class ProcessorI7 : ISystemProcessor
    {
        public string GetProcessor()
        {
            return ComputerProcessor.I7.ToString();
        }
    }


Adding Abstract Interface



Under folder AbstractInterface add a factory interface.

    public interface IComputerFactory
    {
        ISystemBrand SystemBrand();
        ISystemProcessor SystemProcessor();
        ISystemType SystemType();
    }


Adding Concrete Factory Classes


Under folder ConcreateFactory create the factory classes by implmenting IComputeFactory interface. These are the classes which will return the appropriate ConcreteProduct objects.

    public class ComputerDellFactory : IComputerFactory
    {
        public ISystemBrand SystemBrand()
        {
            return new SystemDell();
        }

        public ISystemProcessor SystemProcessor()
        {
            return new ProcessorI5();
        }

        //make this virtual since ComputerDellLaptopFactory will inherit from ComputerDellFactory
        public virtual ISystemType SystemType()
        {
            return new SystemTypeDesktop();
        }
    }

    public class ComputerDellLaptopFactory : ComputerDellFactory
    {
        public override ISystemType SystemType()
        {
            return new SystemTypeLaptop();
        }
    }

    public class ComputerMacFactory : IComputerFactory
    {
        public ISystemBrand SystemBrand()
        {
            return new SystemMac();
        }

        public ISystemProcessor SystemProcessor()
        {
            return new ProcessorI7();
        }

        //making this virtual since ComputerMacbookFactory will inherit from This factory and we'll overwrite the SystemType there
        public virtual ISystemType SystemType()
        {
            return new SystemTypeDesktop();
        }
    }

    public class ComputerMacBookFactory : ComputerMacFactory
    {
       
        public override ISystemType SystemType()
        {
            return new SystemTypeLaptop();
        }
    }

and finally implement the system factory which will return the appropriate factory object depending upon the employee type and designation.

    public class EmpSystemFactory
    {
        public IComputerFactory Create(EmpData emp)
        {
            IComputerFactory factory = null;

            switch (emp.EmployeeType)
            {
                case EmployeeType.Permanent:
                    factory = emp.Designation == EmployeeDesignation.Manager ?
                        new ComputerMacBookFactory() :
                        new ComputerMacFactory();
                    break;
                case EmployeeType.Contract:
                    factory = emp.Designation == EmployeeDesignation.Manager ?
                        new ComputerDellLaptopFactory() :
                        new ComputerDellFactory();
                    break;
            }
            return factory;
        }
    }


Add Client



Under the Client folder add EmpSystemManager class. This client will be responsible for giving us the exact string which we need.


    public class EmpSystemManager
    {
        private readonly IComputerFactory _icf;

        public EmpSystemManager(IComputerFactory iComputerFactory)
        {
            _icf = iComputerFactory;
        }

        public string GetSystemDetails()
        {
            var brand = _icf.SystemBrand().GetBrand();
            var processor = _icf.SystemProcessor().GetProcessor();
            var type = _icf.SystemType().GetSystemType();

            return $"{brand} {type} {processor}";
        }
    }


Integrating the AbstractFactory and Client


 static void Main(string[] args)
        {

            var employees = FillEmployees();

            foreach (var emp in employees)
            {
                //gives back a factory obj
                var factory = new EmpSystemFactory().Create(emp);

                //pass the factory to the manager
                var manager = new EmpSystemManager(factory);

                //fill the details
                emp.ComputerDetails = manager.GetSystemDetails();
            }

            PrintEmployees(employees);

            Console.ReadLine();
        }




Next