Skip to content

Instantly share code, notes, and snippets.

@rootn3rd
Created June 30, 2023 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rootn3rd/91551fb99d683c77393be139a675a233 to your computer and use it in GitHub Desktop.
Save rootn3rd/91551fb99d683c77393be139a675a233 to your computer and use it in GitHub Desktop.
Specification Pattern Example
List<User> list = new()
{
new ("Raymond", 14, false),
new ("Shayna", 34, true),
new ("Vijay",20, false)
};
Console.WriteLine("\nSingle Expr--------");
ExpressionSpecification<User> exp = new(u => u.IsGraduated);
List<User> singleSpec = list.FindAll(exp.IsSatisfiedBy);
singleSpec.ForEach(Console.WriteLine);
Console.WriteLine("\nAnd Expr--------");
ExpressionSpecification<User> underAgeSpec = new(u => u.Age > 16);
List<User> andSpec = list.FindAll(exp.And(underAgeSpec).IsSatisfiedBy);
andSpec.ForEach(Console.WriteLine);
Console.WriteLine("\nOr Expr--------");
ExpressionSpecification<User> nameContainNSpec = new(u => u.Name.Contains('n', StringComparison.OrdinalIgnoreCase));
List<User> orSpec = list.FindAll(exp.Or(nameContainNSpec).IsSatisfiedBy);
orSpec.ForEach(Console.WriteLine);
Console.ReadKey();
record User(string Name, int Age, bool IsGraduated);
interface ISpecification<T>
{
ISpecification<T> And(ISpecification<T> other);
ISpecification<T> Or(ISpecification<T> other);
ISpecification<T> Not(ISpecification<T> other);
bool IsSatisfiedBy(T obj);
}
abstract class CompositeSpecification<T> : ISpecification<T>
{
public ISpecification<T> And(ISpecification<T> other) => new AndSpecification<T>(this, other);
public ISpecification<T> Not(ISpecification<T> other) => new NotSpecification<T>(other);
public ISpecification<T> Or(ISpecification<T> other) => new OrSpecification<T>(this, other);
public abstract bool IsSatisfiedBy(T obj);
}
class AndSpecification<T> : CompositeSpecification<T>
{
public AndSpecification(ISpecification<T> leftSpec, ISpecification<T> rightSpec)
{
Left = leftSpec;
Right = rightSpec;
}
public ISpecification<T> Left { get; }
public ISpecification<T> Right { get; }
public override bool IsSatisfiedBy(T obj) => Left.IsSatisfiedBy(obj) && Right.IsSatisfiedBy(obj);
}
class OrSpecification<T> : CompositeSpecification<T>
{
public OrSpecification(ISpecification<T> leftSpec, ISpecification<T> rightSpec)
{
Left = leftSpec;
Right = rightSpec;
}
public ISpecification<T> Left { get; }
public ISpecification<T> Right { get; }
public override bool IsSatisfiedBy(T obj) => Left.IsSatisfiedBy(obj) || Right.IsSatisfiedBy(obj);
}
class NotSpecification<T> : CompositeSpecification<T>
{
public NotSpecification(ISpecification<T> other) => Other = other;
public ISpecification<T> Other { get; }
public override bool IsSatisfiedBy(T obj) => !Other.IsSatisfiedBy(obj);
}
class ExpressionSpecification<T> : CompositeSpecification<T>
{
public ExpressionSpecification(Func<T, bool> expression) => Expression = expression;
public Func<T, bool> Expression { get; }
public override bool IsSatisfiedBy(T obj) => Expression(obj);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment