In Domain-Driven Design we turn to the specification pattern when we want to check whether an object matches some criteria. I think specification is the oft-forgotten bastard child pattern of the domain model pattern language. I know I frequently turn to queries -- sometimes query objects -- when I want to retrieve a set of some entity to work on.
There are some major drawbacks to using queries. Namely there's a huge testing liability. Say we're using a technology like LINQ or NHibernate HQL or even SQL. Well our query and its various predicates that make up our "where clause" are tied up in a single monolithic statement, each of which needs testing. We end up reach deep into the bucket of compromise to find solutions to deal with this mess. Things like object mothers and fluent fixtures help, but are in no way ideal. Wouldn't it be great if we could create a little vocabulary where each word was verified and tested?
Well, yes, it would. And we can accomplish some of these goals by breeding the specification pattern with one of the original GoF patterns, composite. Other people have written about or demonstrated this concept (extensively) so I'll keep my treatment lean and referential, but I'd encourage you to read on; I'll cover a cute little trick at the end.
Sidebar: Specification is quickly challenging service or component for the title "most overloaded term in software development." Think about it; we've got specifications in Behavior-Driven Development, Domain-Driven Design, and Design by Contract. To be clear I'm using specification in terms of model specifications and the practice of DDD.
Composite Specifications
Let's start with a quick interface, and I'm assuming you've done the background reading on the pattern:
public interface ISpecification
{
bool Matches(T entity);
}
In reality I'd use a generic with a constraint to be sure that "T" in our case is an IEntity. I define an IEntity for my models so I can be sure we have things like an "ID" and a "Version" and can safely but generic constraints in layer supertypes like repositories, builders, factories, etc.
Next I'll define a base class, or layer supertype if you trend fancy, with some special methods and some nested classes. Pay attention to the "And" and "Or" with corresponding nested classes:
public abstract class Specification : ISpecification
{
public abstract bool Matches(T entity);
public Specification And(ISpecification specification)
{
return new AndSpecification(this, specification);
}
public Specification Or(ISpecification specification)
{
return new OrSpecification(this, specification);
}
private class AndSpecification : Specification
{
private readonly ISpecification left;
private readonly ISpecification right;
public AndSpecification(ISpecification left, ISpecification right)
{
this.left = left;
this.right = right;
}
public override bool Matches(T entity)
{
return (left.Matches(entity) && right.Matches(entity));
}
}
private class OrSpecification : Specification
{
private readonly ISpecification left;
private readonly ISpecification right;
public OrSpecification(ISpecification left, ISpecification right)
{
this.left = left;
this.right = right;
}
public override bool Matches(T entity)
{
return (left.Matches(entity) || right.Matches(entity));
}
}
}
And I suppose we'll make a sample entity and a couple specifications. These are admittedly very anemic (setters are usually work of the devil) and contrived. It's illustrative folks!
public class Customer
{
public decimal TotalOrders { get; set; }
public bool Active { get; set; }
}
public class StatusSpecification : Specification
{
private readonly bool active;
internal StatusSpecification(bool active)
{
this.active = active;
}
public override bool Matches(Customer entity)
{
return entity.Active == active;
}
}
public class TotalOrdersSpecification : Specification
{
private readonly decimal amount;
internal TotalOrdersSpecification(decimal amount)
{
this.amount = amount;
}
public override bool Matches(Customer entity)
{
return (entity.TotalOrders > amount);
}
}
With all this in place I can now chain specifications together to achieve a composite -- if not somewhat awkward and noisy -- syntax. Example? Okay, sure:
var specification =
new TotalOrdersSpecification(42m).And(new StatusSpecification(true));
var customer1 = new Customer() { Active = false, TotalOrders = 41m};
Console.WriteLine(specification.Matches(customer1));
// False
var customer2 = new Customer() { Active = true, TotalOrders = 43m};
Console.WriteLine(specification.Matches(customer2));
// True
Console.ReadLine();
Bring the Hotness
Update Nigel Sampson describes a similar technique using lamdas and operator overloading. It's a slightly different spin on the same problem, but he also uses the operator overloading trick for clarity. I'd encourage you to check it out; his blog is new but there's some good stuff there already.
I have some issues with the syntax immediately above. Namely we've got a pretty limited means of expressing complex orders of operation in our specifications. If, in pursuit of complex expressions, we pull the AndSpecification and OrSpecification out then we get an even noisier syntax:
var specification =
new AndSpecification(..., ...).Or(...);
I think I can do better. If we're pursuing the DDD goal of "the model is the code" and we're trying to bring our model to evermore expressive heights we need to eliminate some of the parenthesis junk. We can do this by using that most magical (and dangerous) feature of C#, operator overloading.
Let's send my specification base class to wardrobe and makeup, stat!
public abstract class SexySpecification : ISpecification
{
public abstract bool Matches(T entity);
private class AndSpecification : SexySpecification
{
private readonly ISpecification left;
private readonly ISpecification right;
public AndSpecification(ISpecification left, ISpecification right)
{
this.left = left;
this.right = right;
}
public bool Matches(T entity)
{
return (left.Matches(entity) && right.Matches(entity));
}
}
private class OrSpecification : SexySpecification
{
private readonly ISpecification left;
private readonly ISpecification right;
public OrSpecification(ISpecification left, ISpecification right)
{
this.left = left;
this.right = right;
}
public override bool Matches(T entity)
{
return (left.Matches(entity) || right.Matches(entity));
}
}
private class NotSpecification : SexySpecification
{
private readonly ISpecification specification;
public NotSpecification(ISpecification specification)
{
this.specification = specification;
}
public override bool Matches(T entity)
{
return !(specification.Matches(entity));
}
}
public static SexySpecification operator &(SexySpecification left, ISpecification right)
{
return new AndSpecification(left, right);
}
public static SexySpecification operator |(SexySpecification left, ISpecification right)
{
return new OrSpecification(left, right);
}
public static SexySpecification operator !(SexySpecification specification)
{
return new NotSpecification(specification);
}
}
Now we have a much more natural and terse syntax. You have to use the & and | operators because you can't overload binary operators such as && and ||. I'm OK with this; I get less line noise:
var specification =
new TotalOrdersSpecification(42m) & new StatusSpecification(true);
Admittedly it's a little thing, but as we move into more complex composite specifications little differences like these really start to reduce the mental tax associated with parsing a model which, to me, is a big, big deal. And, when all is said and done, isn't it the little things in life that really matter?
Posted
Sun, Jun 15 2008 2:48 PM
by
Dave Laribee