This article is a continuation of my first article on the Specification Pattern in C#. I recommend you to read it for a better understanding of background concepts.
In the first part of this article, I introduced the specification pattern and how it can help to avoid violating the ‘O’ of SOLID principles of programming. Here, I’ll basically show how two or more specifications can be combined together to achieve more interesting results.
The simplest form of composite specifications are,
- AND Specification (Returns true when all conditions are met)
- OR Specification (Returns true when one or more conditions are met)
- NOT Specification (Returns true when a condition is not met)
- NOR Specification (Returns true when all conditions are NOT met)
- NAND Specification (Returns true when one or more conditions are NOT met)
There are actually other ways in which the conditions can be combined and evaluated but these are the easiest to comprehend. For a deeper understanding, you can read about Logic gates. However, I have put together a summary of their truth tables below (for a simple case of two conditions).
| Condition 1 | Condition 2 | AND | OR | NOT | NOR | NAND |
| T | T | T | T | F | F | F |
| T | F | F | T | F | F | T |
| F | T | F | T | T | F | T |
| F | F | F | F | T | T | T |
Now, let’s apply them to our specification pattern by combining conditions.
First, we create an abstract CompositeSpecification class that implements the IEmployeeSpecification interface. This serves as the base class from which all our composite specifications inherit. It has a constructor that takes in two parameters of type IEmployeeSpecification (which, of course, can also be a CompositeSpecification and this can be very useful especially when we need to cascade composite specifications or create a composite specification with more than two parameters).
Take a look at the code listing below. I have excluded some details such as the Employee class (as seen in part one of the article - Specification Pattern in C#) for the purpose of brevity.
- publicinterfaceIEmployeeSpecification {
- bool IsSatisfiedBy(Employee employee);
- }
- publicclassEmployeeDepartmentSpecification: IEmployeeSpecification {
- privatereadonlystring _department;
- public EmployeeDepartmentSpecification(string depatrment) {
- _department = depatrment;
- }
- publicbool IsSatisfiedBy(Employee employee) {
- return employee.Department.Equals(_department);
- }
- }
- publicclassEmployeeYearSpecification: IEmployeeSpecification {
- privatereadonlyint _year;
- public EmployeeYearSpecification(int year) {
- _year = year;
- }
- publicbool IsSatisfiedBy(Employee employee) {
- return employee.YearOfResumption.Equals(_year);
- }
- }
- publicclassGetEmployee {
- publicstaticList < Employee > GetEmployeeBy(IEmployeeSpecification specification, Employee[] employees) {
- List < Employee > NeededEmployees = newList < Employee > ();
- foreach(Employee employee in employees) {
- if (specification.IsSatisfiedBy(employee)) {
- NeededEmployees.Add(employee);
- }
- }
- return NeededEmployees;
- }
- }
- // COMPOSITE SPECIFICATIONS
- publicabstractclassCompositeSpecification: IEmployeeSpecification {
- protectedIEmployeeSpecification specification1;
- protectedIEmployeeSpecification specification2;
- public CompositeSpecification(IEmployeeSpecification spec1, IEmployeeSpecification spec2) {
- specification1 = spec1;
- specification2 = spec2;
- }
- publicabstractbool IsSatisfiedBy(Employee employee);
- }
- publicclassAndSpecification: CompositeSpecification {
- public AndSpecification(IEmployeeSpecification spec1, IEmployeeSpecification spec2): base(spec1, spec2) {}
- publicoverridebool IsSatisfiedBy(Employee employee) {
- return specification1.IsSatisfiedBy(employee) && specification2.IsSatisfiedBy(employee);
- }
- }
- publicclassOrSpecification: CompositeSpecification {
- public OrSpecification(IEmployeeSpecification spec1, IEmployeeSpecification spec2): base(spec1, spec2) {}
- publicoverridebool IsSatisfiedBy(Employee employee) {
- return specification1.IsSatisfiedBy(employee) || specification2.IsSatisfiedBy(employee);
- }
- }
- publicclassNORSpecification: CompositeSpecification {
- public NORSpecification(IEmployeeSpecification spec1, IEmployeeSpecification spec2): base(spec1, spec2) {}
- publicoverridebool IsSatisfiedBy(Employee employee) {
- return !(specification1.IsSatisfiedBy(employee) || specification2.IsSatisfiedBy(employee));
- }
- }
- publicclassNANDSpecification: CompositeSpecification {
- public NANDSpecification(IEmployeeSpecification spec1, IEmployeeSpecification spec2): base(spec1, spec2) {}
- publicoverridebool IsSatisfiedBy(Employee employee) {
- return !(specification1.IsSatisfiedBy(employee) && specification2.IsSatisfiedBy(employee));
- }
- }
- publicclassNotSpecification: IEmployeeSpecification {
- privateIEmployeeSpecification specification;
- public NotSpecification(IEmployeeSpecification spec) {
- specification = spec;
- }
- publicbool IsSatisfiedBy(Employee employee) {
- return !specification.IsSatisfiedBy(employee);
- }
- }


Chittaranjan SwainPosted Nov 20, 2019, 8:39 AM
Nice one....
Davide BellonePosted Dec 5, 2018, 6:56 AM
Wow, nice article! Are you planning to create another article to show how you handle specifications like employee.YearOfResumption > 2005 ?