Specification Pattern
December 5, 2012 2 Comments
The Specification Pattern. Yet another one I find useful in daily business. But a few enhancements can make it even more useful and a lot nicer to handle.
1, 2, many!
The CompositeSpecification
is most often implemented with two fields for spec1
and spec2
or left
and right
. The AndSpecification
and OrSpecifications
evaluate these fields with their respective operator.
public class Or<T> : CompositeSpecification<T> { // ... public override bool IsSatisfiedBy(T candidate) { return spec1.IsSatisfiedBy(candidate) || spec2.IsSatisfiedBy(candidate); } }
This can result in a degenerated tree structure when you link many specifications with the same operator (e.g. a.Or(b).Or(c)...Or(n)
).
To avoid this I decided to change the behavior of the composite. Instead of two fields it uses a list of children. If you try to link two specifications the code checks wether either one of them is of the same type as the composite and adds the other composites children instead of the composite itself to the list.
Sounds more complicated than it is. Let’s see some code.
public abstract class CompositeSpecification<T> : Specification<T> { private readonly List<Specification<T>> children; public CompositeSpecification() { this.children = new List<Specification<T>>(); } public IEnumerable<Specification<T>> Children { get { return children; } } public int Count { get { return this.children.Count; } } protected void Add(Specification<T> specification) { this.children.Add(specification); } protected void AddRange(IEnumerable<Specification<T>> specifications) { this.children.AddRange(specifications); } } public class Or<T> : CompositeSpecification<T> { public Or(Specification<T> specification, Specification<T> other) { this.Include(specification); this.Include(other); } public override string Description { get { return " || "; } } public override bool IsSatisfiedBy(T candidate) { foreach (Specification<T> specification in this.Children) { if (specification.IsSatisfiedBy(candidate)) { return true; } } return false; } private void Include(Specification<T> specification) { Or<T> or = specification as Or<T>; if (or != null) { this.AddRange(or.Children); } else { this.Add(specification); } } }
And this is how two specifications can be linked with an Or
operator.
public abstract class Specification<T> { // ... public abstract bool IsSatisfiedBy(T candidate); public Specification<T> Or(Specification<T> other) { return new Or<T>(this, other); } }
In the end this will put all successive Or’s in a single list, which makes finding the right specification in the tree a lot easier.
What do we have?
Generating a human readable representation of the specification tree can be tedious but is often beneficial if you need to see “what you have”. The easiest way to traverse a tree structure is a Visitor. The same pattern is used by Microsoft in their expression trees.
We add an Accept
method and a property for the description of the specification to the base classes
public abstract class Specification<T> { // ... public abstract string Description { get; } public virtual void Accept(SpecificationVisitor<T> visitor) { visitor.Visit(this); } } public abstract class CompositeSpecification<T> : Specification<T> { // ... public override void Accept(SpecificationVisitor<T> visitor) { visitor.Visit(this); } } public class Or<T> : CompositeSpecification<T> { // ... public override string Description { get { return " || "; } } }
Define a base class for the SpecificationVisitor
public abstract class SpecificationVisitor<T> { public abstract void Visit(Specification<T> specification); public abstract void Visit(CompositeSpecification<T> composite); }
And the implementation of a PrettyPrinter
becomes as simple as that
public class PrettyPrinter<T> : SpecificationVisitor<T> { private readonly StringBuilder sb; public PrettyPrinter() { this.sb = new StringBuilder(250); } public override void Visit(Specification<T> specification) { this.sb.Append(specification.Description); } public override void Visit(CompositeSpecification<T> composite) { this.sb.Append("("); foreach (Specification<T> child in composite.Children) { child.Accept(this); this.sb.Append(composite.Description); } int l = composite.Description.Length; this.sb.Remove(sb.Length - l, l); this.sb.Append(")"); } public override string ToString() { return this.sb.ToString(); } }
And this gives you a nice and friendly printout of your graph
var spec = new AlwaysFalse().Or(new AlwaysTrue().And(new Odd())).Or(new AlwaysTrue()); var printer = new PrettyPrinter<T>(); spec.Accept(printer); string friendly = printer.ToString(); // (false || (true && Odd) || true)
First time I tried to attach some zipped source code and found out that WordPress won’t let me… Get the source code here (project TecX.Common folder Specifications and the test suite that puts them to use in TecX.Common.Test).