Friday, 6 July 2018

Factory Method





We'll be creating an Abstract class since we need to calculate
  • Rent allowance for the permanent employee
  • Medical allowance for the contract employee
This example builds on top of Simple Factory Advance example. 

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

Basic Setup

 

 

Create Enum for the EmployeeType


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

Create Data Model Class 


This model will represent a database table, which we'll populate without HourlyPay and Bonus since this will be specific to each employee type.

   public class Data
    {
        public string Name { get; set; }
        public string Department { get; set; }
        public decimal? HourlyPay { get; set; }
        public decimal? Bonus { get; set; }
        public decimal? RentAllowance { get; set; }
        public decimal? MedicalAllowance { get; set; }
        public EmployeeType EmployeeType { get; set; }
    }

Fill Employees and Display Employees



Since this is a console app, fill data and write to console

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

            var employees = FillEmployees();

            PrintEmployees(employees);

            Console.ReadLine();
        }

        private static List FillEmployees()
        {
            var employees = new List
            {
                new Data{Name = "Jason", Department = "IT", EmployeeType = EmployeeType.Contract },
                new Data{Name = "Brian", Department = "IT", 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()}HourlyPay{Tab()}Bonus{Tab()}Rent{Tab()}Medical";
            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();
                message += emp.HourlyPay.HasValue
                    ? emp.HourlyPay.GetValueOrDefault().ToString("N0") + Tab() + Tab()
                    : "" + Tab() + Tab();
                message += emp.Bonus.HasValue
                    ? emp.Bonus.GetValueOrDefault().ToString("N0") + Tab()
                    : "" + Tab() ;
                message += emp.RentAllowance.HasValue
                    ? emp.RentAllowance.GetValueOrDefault().ToString("N0") + Tab()
                    : "" + Tab() ;
                message += emp.MedicalAllowance.HasValue
                    ? emp.MedicalAllowance.GetValueOrDefault().ToString("N0")
                    : "";
                Console.WriteLine(message);
                row += 1;
            }

        }


Common Pieces Setup


We'll work with an interface for the common pieces. The interface will get applied to ContractEmployeeManager or PermanentEmployeeManager. It will have two methods which are common to both types of employees. RentAllowence and MedicalAllowence will not be part of the interface since these are specific to a specific employee and detailed towards the top.

IEmployeeManager Interface


    public interface IEmployeeManager
    {
        decimal GetBonus();
        decimal GetPay();
    }


PermanentEmployeeManager 


GetHouseAllowence is specific to permanent employee so is not part of the above interface.

    public class PermanentEmployeeManager : IEmployeeManager
    {
        public decimal GetBonus()
        {
            return 30;
        }

        public decimal GetPay()
        {
            return 50;
        }

        public decimal GetHouseAllowance()
        {
            return 10;
        }
    }


ContractEmployeeManager


GetMedicalAllowance is specific to contract employee so is not part of the above interface.

    public class ContractEmployeeManager : IEmployeeManager
    {
        public decimal GetBonus()
        {
            return 15;
        }

        public decimal GetPay()
        {
            return 80;
        }

        public decimal GetMedicalAllowance()
        {
            return 8;
        }
    }


Abstract and Factory Classes Setup


Since we have two very specific methods for both contract and permanent employees, we'll work with an abstract class and provide an abstract method to fill in these.


Abstract Class BaseEmployeeFactory


    //BaseEmployeeFactory abstract class will be inherited by
    //1. ContractEmployeeFactory => which in turn will use ContractEmployeeManager
    //2. PermanentEmployeeFactory => which in turn will use PermanentEmployeeManager

    public abstract class BaseEmployeeFactory
    {
        protected EmpData _empData;

        public BaseEmployeeFactory(EmpData empData)
        {
            _empData = empData;
        }

        public EmpData ApplyBonusPay()
        {
            //Create abstract method will give us the appropriate contract or permanent employee object using the appropriate Factory class
            //once we have the object then we'll get the bonus and hourly pay since these are in both the Manager classes

            //Rent allowance and medical allowance will be handled separately
            //Rent allowance for the permanent employee only
            //Medical allowance for the contract employee
            //the Create abstract method will fill the _empData

            var empManager = this.Create();

            _empData.Bonus = empManager.GetBonus();
            _empData.HourlyPay = empManager.GetPay();
            return _empData;
        }

        public abstract IEmployeeManager Create();
    }


ContractEmployeeFactory Class


It will fill in the medical allowance and then will return the object to ContractEmployeeManager

    public class ContractEmployeeFactory : BaseEmployeeFactory
    {
        public ContractEmployeeFactory(EmpData empData) : base(empData)
        {
        }

        public override IEmployeeManager Create()
        {
            var manager = new ContractEmployeeManager();

            //in base
            _empData.MedicalAllowance = manager.GetMedicalAllowance();
            return manager;
        }
    }


PermanentEmployeeFactory Class


It will fill in the rent allowance and then  will return the object to PermanentEmployeeManager

    public class PermanentEmployeeFactory : BaseEmployeeFactory
    {
        public PermanentEmployeeFactory(EmpData empData) : base(empData)
        {
        }

        public override IEmployeeManager Create()
        {
            var manager = new PermanentEmployeeManager();

            //in base
            _empData.RentAllowance = manager.GetHouseAllowance();
            return manager;
        }
    }


EmpFactory Class to Tie All Togther


EmpFactory class will be our factory class and will return the appropriate object depending upon the employee type.

    public class EmpFactory
    {
        public BaseEmployeeFactory CreateFactory(EmpData empData)
        {
            BaseEmployeeFactory factory = null;
            if (empData.EmployeeType == EmployeeType.Contract)
            {
                factory = new ContractEmployeeFactory(empData);
            }
            else if (empData.EmployeeType == EmployeeType.Permanent)
            {
                factory = new PermanentEmployeeFactory(empData);
            }
            return factory;
        }
    }


Filling the Pieces and Displaying Data


        static void Main(string[] args)
        {

            var employees = FillEmployees();

            foreach (var emp in employees)
            {
                //EmpFactory
                var empFactory = new EmpFactory();

                //returns object as BaseEmployeeFactory
                var factory = empFactory.CreateFactory(emp);

                //we have either object to ContractEmployeeFactory or PermanentEmployeeFactory
                factory.ApplyBonusPay();
            }

            PrintEmployees(employees);

            Console.ReadLine();
        }



Next