Jump to content

Specification

From Knowledge Base
Revision as of 11:58, 18 January 2025 by Chr1ss (talk | contribs) (Created page with "= Specification Pattern = == Overview == The '''Specification Pattern''' is a software design pattern used to encapsulate business rules, logic, or criteria into a reusable, combinable, and testable format. It provides a clear and modular way to evaluate whether objects meet certain conditions. This pattern is particularly useful in domains with complex validation or filtering requirements. == Key Concepts == * '''Encapsulation of Criteria''': Encapsulates rules or con...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Specification Pattern

Overview

The Specification Pattern is a software design pattern used to encapsulate business rules, logic, or criteria into a reusable, combinable, and testable format. It provides a clear and modular way to evaluate whether objects meet certain conditions. This pattern is particularly useful in domains with complex validation or filtering requirements.

Key Concepts

  • Encapsulation of Criteria: Encapsulates rules or conditions into specification objects.
  • Composability: Allows combining multiple specifications using logical operators such as `AND`, `OR`, and `NOT`.
  • Reusability: Specifications can be reused across different parts of an application.

Benefits

  • Improves code readability and maintainability.
  • Promotes single responsibility by decoupling business rules from domain objects.
  • Enables dynamic criteria evaluation at runtime.
  • Simplifies unit testing for business rules.

Example Implementation

Below is an example of the Specification Pattern in C# for a domain involving filtering Products.

using System;
using System.Linq.Expressions;

// Specification Interface
public interface ISpecification<T>
{
    bool IsSatisfiedBy(T entity);
    ISpecification<T> And(ISpecification<T> other);
    ISpecification<T> Or(ISpecification<T> other);
    ISpecification<T> Not();
}

// Base Specification Class
public abstract class Specification<T> : ISpecification<T>
{
    public abstract Expression<Func<T, bool>> ToExpression();

    public bool IsSatisfiedBy(T entity)
    {
        var predicate = ToExpression().Compile();
        return predicate(entity);
    }

    public ISpecification<T> And(ISpecification<T> other) =>
        new AndSpecification<T>(this, other);

    public ISpecification<T> Or(ISpecification<T> other) =>
        new OrSpecification<T>(this, other);

    public ISpecification<T> Not() =>
        new NotSpecification<T>(this);
}

// Concrete Specification
public class ProductHasMinimumPriceSpecification : Specification<Product>
{
    private readonly decimal _minPrice;

    public ProductHasMinimumPriceSpecification(decimal minPrice)
    {
        _minPrice = minPrice;
    }

    public override Expression<Func<Product, bool>> ToExpression()
    {
        return product => product.Price >= _minPrice;
    }
}

// Example of Combining Specifications
public class AndSpecification<T> : Specification<T>
{
    private readonly ISpecification<T> _left;
    private readonly ISpecification<T> _right;

    public AndSpecification(ISpecification<T> left, ISpecification<T> right)
    {
        _left = left;
        _right = right;
    }

    public override Expression<Func<T, bool>> ToExpression()
    {
        var leftExpression = _left.ToExpression();
        var rightExpression = _right.ToExpression();
        var parameter = Expression.Parameter(typeof(T));
        var body = Expression.AndAlso(
            Expression.Invoke(leftExpression, parameter),
            Expression.Invoke(rightExpression, parameter));
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }
}

// Example Usage
public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class Program
{
    public static void Main()
    {
        var expensiveProductSpec = new ProductHasMinimumPriceSpecification(100);
        var product = new Product { Name = "Laptop", Price = 150 };

        if (expensiveProductSpec.IsSatisfiedBy(product))
        {
            Console.WriteLine($"{product.Name} meets the criteria.");
        }
        else
        {
            Console.WriteLine($"{product.Name} does not meet the criteria.");
        }
    }
}

Advantages

  • Decouples complex rules from business entities.
  • Supports runtime evaluation and dynamic rule composition.
  • Encourages clean architecture and separation of concerns.

Use Cases

  • Validation: Checking whether an object satisfies certain rules.
  • Filtering: Querying data with complex criteria.
  • Authorization: Determining user permissions based on rules.

Related Patterns

  • Strategy Pattern: Both patterns encapsulate behavior but serve different purposes.
  • Interpreter Pattern: Both patterns deal with logic encapsulation, though Specification focuses on evaluation rather than translation.

Further Reading