DEV Community

Cover image for Top 6 Design Patterns
Daniel Azevedo
Daniel Azevedo

Posted on

Top 6 Design Patterns

Hi devs,

Design patterns can help structure and optimize code, especially in complex systems like HR software, which often deals with managing employee records, processing payroll, and tracking performance. Here’s a look at some popular design patterns in C#, focusing on their uses in HR applications.


1. Singleton Pattern

  • Use Case: Centralized Configuration and Database Connection
  • Purpose: Ensures a class has only one instance and provides a global point of access to it.
  • Example: HR systems often need to maintain a single, consistent connection to a central database. A Singleton pattern could ensure only one instance of the database connection is used, minimizing memory usage and enhancing performance.
   public class DatabaseConnection
   {
       private static DatabaseConnection _instance;
       private static readonly object _lock = new object();

       private DatabaseConnection() { }

       public static DatabaseConnection Instance
       {
           get
           {
               lock (_lock)
               {
                   if (_instance == null)
                   {
                       _instance = new DatabaseConnection();
                   }
                   return _instance;
               }
           }
       }
   }
Enter fullscreen mode Exit fullscreen mode

2. Factory Pattern

  • Use Case: Generating Employee Profiles Based on Role
  • Purpose: Creates objects without specifying the exact class of object that will be created.
  • Example: Different employee roles may require different types of onboarding processes. A Factory pattern can dynamically create the appropriate onboarding objects for different roles, whether full-time, part-time, or contractor.
   public interface IEmployee
   {
       void DisplayDetails();
   }

   public class FullTimeEmployee : IEmployee
   {
       public void DisplayDetails() => Console.WriteLine("Full-Time Employee Profile");
   }

   public class Contractor : IEmployee
   {
       public void DisplayDetails() => Console.WriteLine("Contractor Profile");
   }

   public class EmployeeFactory
   {
       public static IEmployee CreateEmployee(string type)
       {
           return type switch
           {
               "FullTime" => new FullTimeEmployee(),
               "Contractor" => new Contractor(),
               _ => throw new ArgumentException("Invalid Employee Type")
           };
       }
   }
Enter fullscreen mode Exit fullscreen mode

3. Observer Pattern

  • Use Case: Notifying Stakeholders of HR Changes
  • Purpose: Allows one object (subject) to notify other objects (observers) about changes without directly referencing them.
  • Example: An HR application can notify relevant departments (Payroll, Benefits) when an employee's status changes (e.g., promotion or termination).
   public class Employee
   {
       private readonly List<IObserver> _observers = new List<IObserver>();
       public string Status { get; private set; }

       public void UpdateStatus(string status)
       {
           Status = status;
           NotifyObservers();
       }

       public void Attach(IObserver observer) => _observers.Add(observer);
       private void NotifyObservers()
       {
           foreach (var observer in _observers)
               observer.Update(Status);
       }
   }

   public interface IObserver
   {
       void Update(string status);
   }

   public class PayrollDepartment : IObserver
   {
       public void Update(string status) => Console.WriteLine($"Payroll updated: {status}");
   }
Enter fullscreen mode Exit fullscreen mode

4. Facade Pattern

  • Use Case: Simplifying Onboarding Process
  • Purpose: Provides a simplified interface to a complex subsystem.
  • Example: The onboarding process in HR might involve multiple steps, like setting up payroll, benefits, and IT access. A Facade pattern can bundle these steps into a single onboarding interface, making it easier for the HR team.
   public class OnboardingFacade
   {
       private readonly PayrollService _payroll = new PayrollService();
       private readonly BenefitsService _benefits = new BenefitsService();
       private readonly ITAccessService _itAccess = new ITAccessService();

       public void OnboardNewEmployee(string employeeId)
       {
           _payroll.Setup(employeeId);
           _benefits.Setup(employeeId);
           _itAccess.GrantAccess(employeeId);
       }
   }
Enter fullscreen mode Exit fullscreen mode

5. Strategy Pattern

  • Use Case: Calculating Payroll Based on Employee Type
  • Purpose: Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
  • Example: Different payroll calculations may be needed for salaried employees, hourly employees, and contractors. The Strategy pattern allows HR to dynamically apply the right calculation method.
   public interface IPayrollStrategy
   {
       decimal Calculate(decimal basePay);
   }

   public class SalariedPayroll : IPayrollStrategy
   {
       public decimal Calculate(decimal basePay) => basePay;
   }

   public class HourlyPayroll : IPayrollStrategy
   {
       public decimal Calculate(decimal hours) => hours * 20;
   }

   public class PayrollContext
   {
       private readonly IPayrollStrategy _strategy;

       public PayrollContext(IPayrollStrategy strategy) => _strategy = strategy;

       public decimal ExecuteCalculation(decimal basePay) => _strategy.Calculate(basePay);
   }
Enter fullscreen mode Exit fullscreen mode

Why Design Patterns Matter in HR Systems

By implementing these patterns in HR applications, developers can improve code structure, facilitate easier maintenance, and create more scalable systems. As HR systems grow, they become increasingly complex, so patterns like these help keep systems manageable.

Keep coding

Top comments (0)