Specification Pattern In C#

First, I’d like to say that I wrote this article because of the difficulty I faced when I tried to research the specification pattern. I saw a lot of confusing videos and articles online. I was eventually able to get the hang of it so I decided to take the time to document it to help others. Moreover, I’ve gained a lot from C# Corner; now, it’s my turn to give back.

This lesson has been divided into two parts. In this part, I will introduce you to the whole idea of the specification pattern, and in part two I will talk about composite specifications.

Okay, what’s the specification pattern about? To answer this question, it’s important to note that every design pattern seeks to solve a problem developers encounter frequently, as well as ensure “clean coding”. The specification pattern helps to ensure that the ‘O’ in SOLID principles is adhered to, i.e. software entities should be open to expansion and closed to modification.

Take a look at the code listing below,

  1. public class GetEmployee  
  2. {  
  3.     public Employee[] GetEmployeesByName(string name)  
  4.     {  
  5.     }  
  6.   
  7.     public Employee[] GetEmployeesByDepartment(string department)  
  8.     {  
  9.     }  
  10.   
  11.     public Employee[] GetEmployeesByID(string ID)  
  12.     {  
  13.     }  
  14.   
  15.     public Employee[] GetEmployeesByYear(string ID)  
  16.     {  
  17.     }  
  18. }  

Of course, we’re assuming that an Employee class has been created somewhere. In the above example, the methods in the GetEmployee class are used to filter employees based on certain criteria (Name, department etc). This is an inefficient way to handle the scenario as it directly violates the ‘O’ of SOLID principles. Implementing it this way means that if we need to create a new filter criterion tomorrow, we’d have to tamper with the GetEmployee class by adding another method.

The code listing below shows how elegantly the specification pattern handles the same scenario.

  1. public class Employee  
  2. {  
  3.     public string EmployeeID { get; set; }  
  4.     public string FirstName { get; set; }  
  5.     public string LastName { get; set; }  
  6.     public string Department { get; set; }  
  7.     private int _yearOfResumption;  
  8.     public int YearOfResumption       
  9.     {  
  10.         get { return _yearOfResumption; }  
  11.         set  
  12.         {  
  13.             if (value > DateTime.Now.Year)  
  14.             {  
  15.                 _yearOfResumption= DateTime.Now.Year;  
  16.             }  
  17.             else { _yearOfResumption = value; }  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. public interface IEmployeeSpecification  
  23. {  
  24.     bool IsSatisfiedBy(Employee employee);  
  25. }  
  26.   
  27. public class EmployeeDepartmentSpecification : IEmployeeSpecification  
  28. {  
  29.     private readonly string _department;  
  30.   
  31.     public EmployeeDepartmentSpecification(string depatrment)  
  32.     {  
  33.         _department = depatrment;  
  34.     }  
  35.   
  36.     public bool IsSatisfiedBy(Employee employee)  
  37.     {  
  38.         return employee.Department.Equals(_department);  
  39.     }  
  40. }  
  41.   
  42. public class EmployeeYearSpecification : IEmployeeSpecification  
  43. {  
  44.     private readonly int _year;  
  45.   
  46.     public EmployeeYearSpecification(int year)  
  47.     {  
  48.         _year = year;  
  49.     }  
  50.     public bool IsSatisfiedBy(Employee employee)  
  51.     {  
  52.         return employee.YearOfResumption.Equals(_year);  
  53.     }  
  54. }  
  55.   
  56. public class GetEmployee  
  57. {  
  58.     public static List<Employee> GetEmployeeBy(IEmployeeSpecification specification, Employee[] employees)  
  59.     {  
  60.         List<Employee> NeededEmployees = new List<Employee>();  
  61.   
  62.   
  63.         foreach (Employee employee in employees)  
  64.         {  
  65.             if (specification.IsSatisfiedBy(employee))  
  66.             {  
  67.                 NeededEmployees.Add(employee);  
  68.             }  
  69.         }  
  70.         return NeededEmployees;  
  71.     }  
  72. }  

Using this method, we simply define a boolean IsSatisfied method in an IEmployeeSpecification interface (which serves as a parameter for our GetEmploeeBy method in GetEmployee class) and have all our specifications implement that interface. That way, if we need to add a new specification in the future, we simply define it and have it implement the IEmployeeSpecification interface.

Below is a sample Program.cs file you can use to test the code,

  1. class Program  
  2.     {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             Employee employee1 = new Employee { FirstName="Fidel",Department="Maths", YearOfResumption = 2017};  
  6.             Employee employee2 = new Employee { FirstName = "Francis", Department = "Software", YearOfResumption = 2018 };  
  7.             Employee employee3 = new Employee { FirstName = "Ahmed",Department = "Maths", YearOfResumption = 2018 };  
  8.             Employee employee4 = new Employee { FirstName = "Ebuka",Department = "Software", YearOfResumption = 2017 };  
  9.   
  10.             Employee[] employees = new Employee[] { employee1,employee2,employee3,employee4};  
  11.   
  12.             Console.WriteLine("Software Department");  
  13.             List<Employee> SoftwareEmployees = GetEmployee.GetEmployeeBy(new EmployeeDepartmentSpecification("Software"), employees);  
  14.             foreach (var employee in SoftwareEmployees)  
  15.             {  
  16.                 Console.WriteLine(employee.FirstName);  
  17.             }  
  18.   
  19.             Console.WriteLine();  
  20.   
  21.             Console.WriteLine("Employed in 2017");  
  22.             List<Employee> EmployedIn2017 = GetEmployee.GetEmployeeBy(new EmployeeYearSpecification(2017), employees);  
  23.             foreach (var employee in EmployedIn2017)  
  24.             {  
  25.                 Console.WriteLine(employee.FirstName);  
  26.             }  
  27.   
  28.             Console.ReadKey();  
  29.         }  
  30.     }  

The picture below shows the expected output.

Output

I hope this article has been helpful. You can also read part two of this publication where I discussed composite specifications. Follow me on C# Corner to know when the article is posted. Kindly drop feedback and comments in the Comments section below.


Similar Articles