C# Abstract Factory Design Pattern Tutorial

Intent

Abstract Factory Object-Oriented Programming Design Pattern is a creational design pattern that provides a top-level common interface to create families of related or dependent objects without specifying their concrete classes.

Video Tutorial – C# Abstract Factory Design Pattern

Motivation

Consider a situation where you work in a company to manage employees and their payrolls. The company hires different categories of employees for example Salaried, Hourly, Commission, and hybrid such as Salary + Commission or Salary + Hourly wage. To handle the creation of these varieties of employees Abstract Factory Design Pattern help by providing a top-level Interface that can be used to create each type of Employee without specifying the employee concrete class. The specific employee data can be managed through a course that maintains the constants for Salary, base salary, hourly rate, commission, or other specific data. The EmployeeFactory class can implement a function inherited from the Common Top-Level Interface to create each special Employee Object.

Applicability

  • The System can be independent of how its entities are created and represented.
  • The System can be configured with multiple related or dependent families of entities.
  • The System only exposes the common and public interface and hides the underlying details of concrete classes that manage the creation of entities.

Consequences

  • Isolates the concrete classes because the Abstract Factory design pattern helps control the way objects are created in the application.
  • Abstract Factory is responsible for the creation of entities and the client just uses the function exposed through the Abstract Factory Interface.
  • This design pattern helps adapt the Factory and enables it to be replaced whenever needed because it only appears once in the application. It maintains the abstraction of how the objects are created.
  • This design pattern promotes cohesion between the related objects as it is a good programming practice to use the single abstract factory to create related or dependent entities through an abstract factory instance.
  • The major drawback of the Abstract Factory design pattern is that it is difficult to extend the Abstract Factory to add new entities to be created.

Participants

  • AbstractEmployeeFactory: Interface to expose the functions for the creation of Abstract Employee Entities.
  • EmployeeFactory: The concrete factory that implements the AbstractEmployeeFactory Interface to provide the processes for object creation.
  • AbstractEmployee: Top-Level Abstract Employee Interface that provides function declaration needed for the Employee classes to implement.
  • Employee Classes: Concrete Employee classes that implement the AbstractEmployee interface and provide desired functionality according to the Employee Type.
  • Client: Client code that will create an instance of AbstractEmployeeFactory using the EmployeeFactory concrete class to create different AbstractEmployee objects and interact with them.

Implementation Strategy

  1. Implement factories as Singleton classes because we only need a single factory instance to create related or dependent objects of a family.
  2. It declares an interface that exposes a method to be used to create objects of entities.
  3. The concrete factory implements this interface and provides a way to identify what child or concrete class will be used to create the object. The createInstance() function can take some parameters to perform identification process for object creation.

Code

IEmployee.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// Top Level Abstract Employee Interface that provides a contract
    /// to calculate the salary of any Employee in the Sysetm that
    /// will implement this Interface. This Abstract Employee is used
    /// to create specialized Employee Objects.
    /// </summary>
    internal interface IEmployee
    {
        /// <summary>
        /// Function to calculate the Salary of the Employee
        /// </summary>
        /// <returns></returns>
        double salary();
    }
}

CommissionEmployee.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// Employee that only Take Commission on Sales. Also implements
    /// the IEmployee Interface.
    /// </summary>
    internal class CommissionEmployee : IEmployee
    {
        private double commission;

        /// <summary>
        /// Create a Commission Employee with a given Commission.
        /// </summary>
        /// <param name="commission"></param>
        public CommissionEmployee(double commission)
        {
            this.commission = commission;
        }


        /// <summary>
        /// Function to calculate the Salary of the Employee
        /// </summary>
        /// <returns></returns>
        public double salary()
        {
            return commission;
        }

        /// <summary>
        /// Get the String representation of Employee object.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"Commission Employee: ${salary()}";
        }
    }
}

SalariedEmployee.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// This employee object only takes Salary. Also implements the IEmployee
    /// Interface.
    /// </summary>
    internal class SalariedEmployee : IEmployee
    {
        private double wage;

        /// <summary>
        /// Create a Salaried Employee
        /// </summary>
        /// <param name="wage"></param>
        public SalariedEmployee(double wage)
        {
            this.wage = wage;
        }

        /// <summary>
        /// Function to calculate the Salary of the Employee
        /// </summary>
        /// <returns></returns>
        public double salary()
        {
            return wage;
        }

        /// <summary>
        /// Get the String representation of Employee object.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"Salaried Employee: ${salary()}";
        }
    }
}

HourlyEmployee.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// Hourly employee that maintains the Hours Worked and Hourly
    /// Salary. If hours worked are more than 40, the hourly wage is
    /// 50% more than regular hours. Also implements the IEmployee
    /// Interface.
    /// </summary>
    internal class HourlyEmployee : IEmployee
    {
        private double hourlyWage;
        private double hoursWorked;


        /// <summary>
        /// Create Hourly Employee with hours worked and hourly rate
        /// </summary>
        /// <param name="hourlyWage"></param>
        /// <param name="hoursWorked"></param>
        public HourlyEmployee(double hourlyWage, double hoursWorked)
        {
            this.hourlyWage = hourlyWage;
            this.hoursWorked = hoursWorked;
        }


        /// <summary>
        /// Function to calculate the Salary of the Employee
        /// </summary>
        /// <returns></returns>
        public double salary()
        {
            double wage = 0;

            double hours = hoursWorked;

            // if they are above 40, increase wage 50%
            if(hours > 40)
            {
                wage = (hours - 40) * 1.5 * hourlyWage;
                hours = 40;
            }

            // regular pay
            wage += hours * hourlyWage;

            // return total salary
            return wage;
        }

        /// <summary>
        /// Get the String representation of Employee object.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"Hourly Employee: ${salary()}";
        }
    }
}

BaseCommissionEmployee.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// Base Commission Employee that will take Base Salary
    /// and The commission on Sales. Also implements the IEmployee
    /// Interface.
    /// </summary>
    internal class BaseCommissionEmployee : IEmployee
    {
        private double baseSalary;
        private double commission;

        /// <summary>
        /// constructor to create BaseCommissionEmployee object.
        /// </summary>
        /// <param name="baseSalary"></param>
        /// <param name="commission"></param>
        public BaseCommissionEmployee(double baseSalary, double commission)
        {
            this.baseSalary = baseSalary;
            this.commission = commission;
        }

        /// <summary>
        /// Function to calculate the Salary of the Employee
        /// </summary>
        /// <returns></returns>
        public double salary()
        {
            return baseSalary + commission;
        }

        /// <summary>
        /// Get the String representation of Employee object.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"BaseCommission Employee: ${salary()}";
        }
    }
}

IEmployeeFactory.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// Top-Level Employee Factory Interface that exposes the functions
    /// to the client code to create different types of Employees. This
    /// top-level Abstract Factory Contract will be implemented by the
    /// EmployeeFactory concrete singleton factory class.
    /// </summary>
    internal interface IEmployeeFactory
    {

        /// <summary>
        /// Function to create a Commission based Employee Object.
        /// </summary>
        /// <param name="commission"></param>
        /// <returns></returns>
        IEmployee CreateCommissionEmployee(double commission);


        /// <summary>
        /// Function to create an Hourly Employee
        /// </summary>
        /// <param name="hourlyWage"></param>
        /// <param name="hoursWorked"></param>
        /// <returns></returns>
        IEmployee CreateHourlyEmployee(double hourlyWage, double hoursWorked);

        /// <summary>
        /// Create an Employee with Salary
        /// </summary>
        /// <param name="salary"></param>
        /// <returns></returns>
        IEmployee CreateSalariedEmployee(double salary);

        /// <summary>
        /// Create an Employee with Base Salary and Commission.
        /// </summary>
        /// <param name="baseSalary"></param>
        /// <param name="commission"></param>
        /// <returns></returns>
        IEmployee CreateBaseCommissionEmployee(double baseSalary, double commission);
    }
}

EmployeeFactory.cs

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

namespace AbstractEmployeeFactoryApp
{
    /// <summary>
    /// Employee Factory class that implement the IEmployeeFactory
    /// Interface that is top-level factory interface and provides
    /// the function implementation for the Employee Creation.
    /// </summary>
    internal class EmployeeFactory : IEmployeeFactory
    {
        private static EmployeeFactory factory;
       
        /// <summary>
        /// Restrict the object create by declaring private Constructor
        /// </summary>
        private EmployeeFactory()
        {
            //factory = new EmployeeFactory(); // StackOverFLow
        }

        /// <summary>
        /// Provide a Global Access Point to the Singleton Instance of this Factory.
        /// </summary>
        /// <returns></returns>
        public static EmployeeFactory GetFactory()
        {
            // Check if factory is null
            if(factory == null)
            {
                factory = new EmployeeFactory();
            }

            return factory;
        }

        /// <summary>
        /// Function to create a Commission based Employee Object.
        /// </summary>
        /// <param name="commission"></param>
        /// <returns></returns>
        public IEmployee CreateCommissionEmployee(double commission)
        {
            return new CommissionEmployee(commission);
        }


        /// <summary>
        /// Function to create an Hourly Employee
        /// </summary>
        /// <param name="hourlyWage"></param>
        /// <param name="hoursWorked"></param>
        /// <returns></returns>
        public IEmployee CreateHourlyEmployee(double hourlyWage, double hoursWorked)
        {
            return new HourlyEmployee(hourlyWage, hoursWorked);
        }

        /// <summary>
        /// Create an Employee with Salary
        /// </summary>
        /// <param name="salary"></param>
        /// <returns></returns>
        public IEmployee CreateSalariedEmployee(double salary)
        {
            return new SalariedEmployee(salary);
        }

        /// <summary>
        /// Create an Employee with Base Salary and Commission.
        /// </summary>
        /// <param name="baseSalary"></param>
        /// <param name="commission"></param>
        /// <returns></returns>
        public IEmployee CreateBaseCommissionEmployee(double baseSalary, double commission)
        {
            return new BaseCommissionEmployee(baseSalary, commission);
        }
    }
}

Program.cs

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

namespace AbstractEmployeeFactoryApp
{
    internal class Program
    {
        /// <summary>
        /// Client Code that uses the IEmployeeFactory Interface
        /// object and create Employees using it and display the
        /// Employee details.
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            // Create Instance of EmployeeFactor
            IEmployeeFactory factory = EmployeeFactory.GetFactory();  //singleton Design Pattern

            // Create Employees - Salaried
            IEmployee emp = factory.CreateSalariedEmployee(25000);
            Console.WriteLine("{0}", emp.ToString());

            // Hourly Employee
            emp = factory.CreateHourlyEmployee(13.5, 42);
            Console.WriteLine("{0}", emp.ToString());

            // Create Commission Employee
            emp = factory.CreateCommissionEmployee(19000);
            Console.WriteLine("{0}", emp.ToString());

            emp = factory.CreateBaseCommissionEmployee(5000, 12900);
            Console.WriteLine("{0}", emp.ToString());

            Console.ReadKey();
        }
    }
}

Download Project Files
Views: 22

Leave a Reply

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