Specification: Difference between revisions
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..." |
No edit summary |
||
| Line 14: | Line 14: | ||
* Enables dynamic criteria evaluation at runtime. | * Enables dynamic criteria evaluation at runtime. | ||
* Simplifies unit testing for business rules. | * Simplifies unit testing for business rules. | ||
== Key Points to Focus On == | |||
# '''Expression Trees''': The pattern leverages Expression<Func<T, bool>> to represent conditions as expressions that can be compiled and executed at runtime. This is a powerful feature in C# and .NET, allowing for dynamic filtering and validation logic without hardcoding conditions. | |||
# '''Combining Specifications''': The And, Or, and Not methods allow you to combine different business rules in a flexible way. This modular approach makes it easy to define complex criteria by combining smaller, reusable specifications. | |||
# '''IsSatisfiedBy''': This method evaluates whether an object satisfies the specification's criteria. This is the core of the Specification Pattern, providing a clean and modular way to handle validation logic. | |||
== Example Implementation == | == Example Implementation == | ||
Revision as of 12:04, 18 January 2025
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.
Key Points to Focus On
- Expression Trees: The pattern leverages Expression<Func<T, bool>> to represent conditions as expressions that can be compiled and executed at runtime. This is a powerful feature in C# and .NET, allowing for dynamic filtering and validation logic without hardcoding conditions.
- Combining Specifications: The And, Or, and Not methods allow you to combine different business rules in a flexible way. This modular approach makes it easy to define complex criteria by combining smaller, reusable specifications.
- IsSatisfiedBy: This method evaluates whether an object satisfies the specification's criteria. This is the core of the Specification Pattern, providing a clean and modular way to handle validation logic.
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.