Skip to content

Instantly share code, notes, and snippets.

@trbngr
Created November 12, 2010 02:09
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 trbngr/673616 to your computer and use it in GitHub Desktop.
Save trbngr/673616 to your computer and use it in GitHub Desktop.
Specification Pattern
using System;
namespace ConsoleApplication3
{
internal class Program
{
private static void Main()
{
var chris = new Person {name = "Chris", age = 36};
//Traditional syntax
var startsWithC = Specification.Create<Person>(p => p.name.StartsWith("C"));
var lessThan40 = Specification.Create<Person>(p => p.age < 40);
var specification = startsWithC.And(lessThan40);
Console.Out.WriteLine(specification.IsSatisfiedBy(chris));
//Support lambdas for boolean aggregation.
specification = startsWithC.And(p => p.age < 40);
Console.Out.WriteLine(specification.IsSatisfiedBy(chris));
}
}
internal class Person
{
public int age;
public string name;
}
public static class Specification
{
public static IExpressionSpecification<T> Create<T>(Func<T, bool> func)
{
return new DynamicSpecification<T>(func);
}
}
internal class DynamicSpecification<T> : Specification<T>
{
private readonly Func<T, bool> func;
public DynamicSpecification(Func<T, bool> func)
{
this.func = func;
}
public override bool IsSatisfiedBy(T instance)
{
return func(instance);
}
}
public interface ISpecification<T>
{
bool IsSatisfiedBy(T instance);
ISpecification<T> And(ISpecification<T> specification);
ISpecification<T> Or(ISpecification<T> specification);
ISpecification<T> Not(ISpecification<T> specification);
}
public interface IExpressionSpecification<T> : ISpecification<T>
{
IExpressionSpecification<T> And(Func<T, bool> expression);
IExpressionSpecification<T> Or(Func<T, bool> expression);
IExpressionSpecification<T> Not(Func<T, bool> expression);
}
public abstract class Specification<T> : IExpressionSpecification<T>
{
#region IExpressionSpecification<T> Members
public abstract bool IsSatisfiedBy(T instance);
public ISpecification<T> And(ISpecification<T> specification)
{
return new AndSpecification<T>(this, specification);
}
public ISpecification<T> Or(ISpecification<T> specification)
{
return new OrSpecification<T>(this, specification);
}
public ISpecification<T> Not(ISpecification<T> specification)
{
return new NotSpecification<T>(this, specification);
}
public IExpressionSpecification<T> And(Func<T, bool> expression)
{
return new AndSpecification<T>(this, new DynamicSpecification<T>(expression));
}
public IExpressionSpecification<T> Or(Func<T, bool> expression)
{
return new OrSpecification<T>(this, new DynamicSpecification<T>(expression));
}
public IExpressionSpecification<T> Not(Func<T, bool> expression)
{
return new NotSpecification<T>(this, new DynamicSpecification<T>(expression));
}
#endregion
}
internal abstract class CompositeSpecification<T> : Specification<T>
{
protected CompositeSpecification(ISpecification<T> left, ISpecification<T> right)
{
Left = left;
Right = right;
}
public ISpecification<T> Left { get; private set; }
public ISpecification<T> Right { get; private set; }
}
internal class AndSpecification<T> : CompositeSpecification<T>
{
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
: base(left, right)
{
}
public override bool IsSatisfiedBy(T instance)
{
return Left.IsSatisfiedBy(instance) && Right.IsSatisfiedBy(instance);
}
}
internal class OrSpecification<T> : CompositeSpecification<T>
{
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
: base(left, right)
{
}
public override bool IsSatisfiedBy(T instance)
{
return Left.IsSatisfiedBy(instance) || Right.IsSatisfiedBy(instance);
}
}
internal class NotSpecification<T> : CompositeSpecification<T>
{
public NotSpecification(ISpecification<T> left, ISpecification<T> right)
: base(left, right)
{
}
public override bool IsSatisfiedBy(T instance)
{
return !Left.IsSatisfiedBy(instance) && !Right.IsSatisfiedBy(instance);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment